Cordex Forms - changelog
========================

Entries describe this plugin. Where a fix also applied to the separate Cordex Forms Pro add-on, that is noted; nothing here requires the add-on.

= 1.9.29 =
* Maintenance release. Rebuilt to bake a rotated internal service credential (the KeyWarden APIM subscription key). No functional or code change; no action needed on your part.

= 1.9.28 =
* Maintenance release. Version aligned with the Cordex Forms Pro add-on, which removes its single "Document upload" field in favour of Required documents. No functional change to this free plugin.

= 1.9.27 =
* Fixes a form that rendered on a page but was nowhere in the Shortcodes list - an "orphan": a compiled form whose builder entry was gone, so it could not be previewed, edited or deleted, yet still appeared and still accepted submissions. A [cordex_form] whose id has no entry now shows the same "this form no longer exists" notice a missing one does, and its submission endpoint refuses too, so it can never create a record with no shortcode to manage it.
* Fixes the cause: exporting the configuration dumped every compiled form (orphans included) while importing merged those in but replaced the entry list, so orphans accumulated across export/import cycles. The export now carries only forms that have an entry, and a full-config import reconciles the form store to the imported list - dropping any form the list does not name.
* Cleans up existing orphans on update: when you install this version, any orphaned form definitions already in the database are removed, and a dismissible admin notice names exactly what was removed so the clean-up is never silent. (A safety check means it never runs when no forms have entries yet.)

= 1.9.26 =
* Maintenance release. Version aligned with the Cordex Forms Pro add-on, which fixes its grid designer (the "how these settings look" sample, and a duplicate copy button). No functional change to this free plugin.

= 1.9.25 =
* Fixes a form that could not be saved after a picked field this plugin no longer offers was left in it - for example a form first built with the Pro add-on and then opened here, still carrying a Pro-only column. That field has no checkbox to untick, so there was no way to remove it, and the save was refused by name with no way to act on it. It now appears in the Layout designer's "Unplaced" row as an amber, struck-through chip with its own × button: press it to remove the field, then save. A browser check exercises the removal.

= 1.9.24 =
* The Theme step now shows a live sample. Pick a style theme (or change any colour, button style or message setting) and a small preview right there shows the submit button and the success, error and sending message lines in those exact colours - so you can see what a theme does without opening the full preview.
* The Layout designer is now the clear centrepiece of the builder: it sits in its own bordered card under a "Layout" heading, set apart from the plainer settings around it, so it is easy to find and read as the place you arrange the form. (The same layout treatment ships in the Cordex Forms Pro add-on's designer.)

= 1.9.23 =
* Fixes the preview after a test submission. Submitting a live preview then using "Submit another response" reloaded the preview frame - which failed because the preview document's temporary URL had already been released. It is now held until the preview is closed, so a reload works and you can run another test straight away. A browser check now exercises the reload. (Same fix in the Cordex Forms Pro add-on.)
* The Go Pro page now shows the newer add-on capabilities it was missing: the conditional rules engine, required document sets, de-duplication (match and update instead of creating a duplicate), and hidden fields / default values.
* Wording: the upload malware-scanning feature now reads "can be virus-scanned" rather than "is virus-scanned", since scanning is an Enterprise option you turn on, not something that always runs.

= 1.9.22 =
* Designer layout tidy-up on the Shortcodes screen. All the builder cards now sit at one full width - the "Live shortcode" and "In plain English" cards line up with the config card above them instead of stopping short and leaving a band of empty screen. The Pro upsell note fills its row rather than being capped at the reading width. And the gap between the config card and the "Live shortcode" card is now the same even spacing as every other card, instead of the two being jammed together. A browser check now measures all three so they cannot drift again.

= 1.9.21 =
* The designer's tabs now run horizontally across the top with a full-width panel below - the same layout as the Cordex Forms Pro add-on - instead of a vertical list down the left, and the whole designer now grows to the full working width of the screen so the layout designer and field picker have room.
* When a picked column is not on the chosen table, the message now names it exactly (for example: '"entityimage" is not a column on this table, so it can\'t be included - remove it from the picked fields, then save again.') instead of the old vague "one of the picked fields is not available".
* The live preview no longer goes blank when the form still has such a column: it drops that one column, notes it above the form, and renders (and lets you test) the rest, so you can keep designing while you sort the offending field out.

= 1.9.20 =
* The Shortcodes designer now lays its configuration out as tabs - Form, Fields, Theme, Button, Messages and so on down the left, one panel at a time on the right - the same shape the Cordex Forms Pro add-on's designer uses, so the free and paid designers now look and work the same way. A save that is refused for a field opens the tab that field is on, so the message is never hidden behind another tab.
* The form Preview is bigger, and it is now LIVE: it renders your current design including changes you have not saved yet, so you can arrange the form and see the result without saving first.
* The Preview's Submit button now works. Submitting the preview runs a real end-to-end test through the same path a visitor's submission takes, so you can confirm a form writes to Dataverse before you publish it. (The preview note says so; a test submission is created just like a real one.) The same enlarged, live, testable preview ships in the Cordex Forms Pro add-on.

= 1.9.19 =
* The designer's form Preview now opens in a focused popover that renders your form in an isolated frame - so it looks exactly as it will on the published page, instead of picking up the admin screen's styling. Open it, look it over, close it. (The same popover ships in the Cordex Forms Pro add-on, which also shows a small designer build number so you can tell at a glance when a cached copy of the admin script is being served.)

= 1.9.18 =
* Clearer wording in the Cordex Forms Pro add-on's malware-scanner chooser (an Enterprise feature): each option now says what you can actually do with it - "ready to use", "needs ..." (a detail to enter), or "not installed on this server" - instead of the old, self-contradictory "already on this server - not installed on this server". The easiest options are listed first. The free plugin has no scanner chooser and is unchanged.

= 1.9.17 =
* Saving a draft twice with a file still attached no longer fails with "Could not save your draft." The second save collides with the file already stored under the same slot - an expected, handled case - but on hosts that display database errors, WordPress printed that collision as HTML into the response and the browser could not read the result. The save now suppresses that error display (its own error handling is unchanged), so the response stays clean and the re-save succeeds. Also: the in-progress message on a draft save now reads "Scanning file for malware…" (only when scanning is actually enabled) or "Saving draft…", instead of the Submit button's wording.

= 1.9.16 =
* Fixes a display regression in the Cordex Forms Pro add-on's Fields step, where each ticked column rendered as a full-width bar instead of a checkbox. (The free plugin's own Fields picker was unaffected; this entry is noted here only because the two share a designer.) A browser check now measures the checkbox against the plugin's own stylesheet so the layout cannot regress again.

= 1.9.15 =
* The Fields step keeps every column you tick. Ticking a column and then switching designer tabs - or anything that refreshed the field list - could drop the tick before the form was saved; the picker now holds its selection in a durable model that survives a rebuild, so what you ticked is what is saved. A browser check ticks a column, forces a rebuild and proves the selection survives, so the fault cannot return quietly. (The same fix ships in the Cordex Forms Pro add-on, which adds a Pro-and-higher type filter and live search over the field list.)

= 1.9.14 =
* From a seven-persona review (three visitors, an administrator, and blue/red/black security passes). Two front-end regressions fixed: pressing Enter on a page with an unmet rule/required-document no longer skips the check and turns the page, and the Discard-draft control is legible on every theme. Five accessibility fixes (WCAG 2.1 AA): no sideways scroll on a phone when a draft holds a file, screen-reader announcement and field association for document and rule errors, an accessible name on the popup dialog, and a readable switch when it is off. Plus polish: the success panel's "Submit another response" is a real button, Escape closes a blocking popup as Cancel, and status messages no longer contradict each other.

= 1.9.13 =
* Save draft and Back now look like buttons on every theme. The default styling covered only the primary button, so a theme's bare-button CSS owned the secondary ones - on some themes Save draft rendered as a tiny chip beside a full-size Next. Secondary buttons now share the primary's geometry with a neutral ground; a theme's configured button style still wins exactly as before. The check suite now requires every nav button to share one height and one font size under a hostile theme.

= 1.9.12 =
* The form preview lives in a proper card. The row Preview rendered the whole form on the bare page under the table - no border, no padding, flush against the last row - while the builder's empty "Preview" card sat stranded mid-screen; the Pro designer's preview had no card at all. All three flows now render inside one panel-recipe card that appears only when it has content, and the preview region joined the screen-rhythm and preview checks so it cannot drift back.

= 1.9.11 =
* Compliance and polish release, from a run of the official WordPress Plugin Check tool. Tested up to WordPress 7.1. The changelog keeps its newest entries here with the full history in changelog.txt. Every finding on this plugin's code is fixed or carries a documented justification, the anonymous draft cookie is sanitised through the standard WordPress pipeline (existing drafts unaffected), and the release process now runs a Plugin-Check-equivalent lint over both plugins as a blocking step. The Shortcodes screen's footer - Save row, Live shortcode, In plain English - now sits in one card rhythm with one aligned edge, and a new screen-rhythm check keeps it that way.

= 1.9.10 =
* The "Store attached files in drafts" setting no longer appears in this plugin's designer - it belongs to the separate Pro add-on (whose forms have file fields) and now lives there, offered only on licences that carry a file feature. Nothing else in this plugin changes; a Pro form's stored setting is preserved by saves made here. The add-on's 1.9.10 also brings its in-plugin Guide up to date with everything the 1.9 line added - pages, drafts, rules and their actions, document sets, and files in drafts.

= 1.9.9 =
* From a five-agent sweep (test battery plus three persona walkthroughs). In this plugin: a submission from a resumed draft can no longer vanish behind the anti-bot timing check - the draft's age now counts, server-side, via the timing token (a genuinely instant fill is still refused); anything the form hides now stays hidden regardless of theme CSS; the Next/Submit button gets a clear default primary style (your theme colours, when set, are untouched) and goes full-width on phones; the status line is coloured, clears on page turns, and says "Your draft was restored." on resume; visitors can now DELETE their draft from the form (two-step confirm); a page can be removed in the designer (its sections move to the previous page); the column-widths note now says the save will refuse (and the refusal clears the moment the widths are fixed); draft sub-settings grey out while drafts are off; and the anonymous-draft limit names its timeframe. The add-on's 1.9.9 shows the Documents step only on licences that carry a document destination, offers only the destinations the licence can deliver, preserves a stored document set whose destination has lapsed (read-only, revives with the licence) instead of blocking every save of the form, and converts the slot "required" control to the proper switch.

= 1.9.8 =
* This plugin gains the shared plumbing for files stored in drafts - the sanitiser and the encrypted per-draft storage engine, with anonymous storage capped per visitor, per hour and globally so it cannot be used to fill your database. On its own, nothing changes: this plugin's forms take no file uploads, drafts keep text only, and no new setting appears. The separate Pro add-on's 1.9.8 puts the feature on top - a per-form "Store attached files in drafts" opt-in (off by default) for its file fields, with every attached file checked against the form's upload policy and malware-scanned at the moment of saving, shown on resume as an attached row with Replace and Remove, scanned again at submission and delivered through the normal pipeline; draft files die with their draft (submission, discard, or expiry).

= 1.9.7 =
* Experience release, from two persona walkthroughs (a visitor and an administrator working through the real product). Visitor side: a successful submission now ends the form with a clear success panel (with a "Submit another response" link) instead of resetting it; drafts say plainly - at save and again on resume - that attached files are not stored; a resumed draft opens quietly (no replayed popups) with focus placed sensibly after every page turn; a step-dots indicator joins the "Step N of M" caption; and nav buttons meet the 44px touch guideline on phones. Designer side: column widths that do not total 100 now say so and refuse the save instead of being silently ignored.
* The Pro add-on's 1.9.7 additionally: checks document-slot file types in the browser, gives document slots the rule-card treatment with live duplicate-column warnings, caps the slot Page list at the form's real page count, groups rule actions into "Enforced on the server" and "Visual only" (deriving the split from the engine itself), plain-words the action verbs, and corrects the Rules helper's enforcement over-claim.

= 1.9.6 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on. The add-on's 1.9.6 makes the popup dialog inherit the form's own typography, re-arms an informational popup each time its condition becomes true again, and clarifies the blocking switch's wording ("asked at Next/Submit").

= 1.9.5 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on. The add-on's 1.9.5 makes the popup rule action a true centred modal dialog with a dimmed backdrop (its stylesheet had been attached to a style handle that was never registered, so it silently vanished on every page), and adds an "apply to the label" switch to the highlight, bold and italic rule actions - by default they act on the input box and the text typed into it; flipped, they act on the label.

= 1.9.4 =
* Cosmetic and behaviour fixes from live testing. On a paged form the columns of each page now start and end level, with even card spacing. Page 1 can carry a title like every other page. Nothing else in this plugin changes; the Pro add-on's 1.9.4 additionally fixes the rule builder's popup action (no longer tied to a field, fires as the standard dialog, and an unfinished rule now says exactly what is missing), the highlight rule action (tints the input box, not the label), and the "stops the send" switch geometry.

= 1.9.3 =
* Hardening release from a full multi-agent assessment of 1.9.2 - twenty-one findings fixed, each pinned by a test. In this plugin: new anonymous drafts are now quota-limited per visitor and capped globally, so the drafts table cannot be filled by a bot; a draft save that could not be written now says so instead of claiming success; resuming a draft opens on the page the visitor was entering, never a hidden or completed one; multi-select values survive a draft; the admin Preview shows every page of a paged form; configuration import runs the same definition migration as normal loading; and saving a form in this plugin's designer no longer discards settings that belong to the separate Pro add-on.
* The Pro add-on's 1.9.3 additionally fixes: a legacy rule without a stored id could make a form impossible to submit; document sets now survive a licence lapse and a de-duplicated submission; parked SharePoint files keep their target environment on retry; and the release process now runs the browser checks and sabotage batteries as blocking gates.

= 1.9.2 =
* A form can now be split into pages. The designer assigns sections to pages and gives each page a title; visitors get Back, Next and Submit with a "Step N of M" caption. A single-page form renders exactly as before - nothing changes until the designer creates a second page.
* A paged form can save a draft as the visitor goes - automatically on Next, or by choosing Save draft. Drafts are stored ENCRYPTED on this server, tied to the logged-in user or to a private browser cookie for anonymous visitors, and deleted automatically once the form is submitted or after 30 days of inactivity (the designer can shorten this, never lengthen it). Uploaded files are never stored in a draft; after resuming, files are re-attached.
* The separate Pro add-on's 1.9.2 builds on this: rules run when the visitor presses Next, and a form can demand a specific named set of documents and refuse a submission that is missing one. None of that changes how this plugin behaves on its own.

= 1.9.1 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on. The add-on's 1.9.1 adds three rule actions - refuse with a message, show a popup, ask a three-button question - and fixes the two rule actions that did nothing. One shared formatter in this plugin is used by the add-on's new controls; nothing here behaves differently on its own.

= 1.9.0 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on. The add-on's 1.9.0 is a large release - values, hidden fields and rules now work on record pages as well as submission forms - and none of it changes how this plugin behaves on its own.

= 1.8.65 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on.

= 1.8.64 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on.

= 1.8.63 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on.

= 1.8.62 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on.

= 1.8.61 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.60 =
* No user-facing change in the free plugin; version kept in lockstep with the add-on.

= 1.8.59 =
* Bold and italic labels now actually apply. The setting emitted a rule that lost on specificity to the plugin's own field-label rule, so it could be switched on and saved with no visible effect.

= 1.8.58 =
* Labels placed beside their values now share one column width, so the values line up down the card. Each label previously sized itself to its own text, which started every value in a different place.
* A label too long for the column wraps inside it rather than pushing its own value out of line.

= 1.8.57 =
* A section's LABEL POSITION and its SIDE-BY-SIDE arrangement now survive being saved. The layout sanitiser rebuilt every section from its title, column and fields and silently dropped both, so a form designed with labels beside their values rendered them above - a setting that could be chosen, was stored by the designer, and then died on its way into the definition. This plugin and the add-on each had a copy of that rebuild and both had the fault; there is now one shared rule that both call.
* A page-wide "Field labels" setting on the Fields tab - above, below, left or right - which a section can override. The per-section control's neutral state has always meant "follow the page", and a submission form had no page setting for it to follow.
* Everything returns to labels-above on a phone, where a label column costs width that is not there.

= 1.8.56 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.55 =
* THE FREE PLUGIN NOW DROPS AN INSTRUCTION IT CANNOT CARRY OUT, RATHER THAN POSTING IT. An add-on may attach a private note to a queued submission - a note about how that row should be delivered when its turn comes. If the add-on is deactivated while such rows are still waiting, this plugin's own delivery takes them, and the right thing to do with an instruction nothing can act on is to ignore it and deliver the record: the record is what the visitor submitted and it belongs in Dataverse either way. Those keys are now stripped before the record is sent instead of being posted and refused. A queued item that is not a record at all is still left to fail and park for a human, which is the opposite case and the opposite answer.
* A SAVED FORM DEFINITION NOW CARRIES A VERSION. It never has, which was fine while the shape only ever gained optional settings, and stops being fine the moment a setting changes meaning. The stamp is written on save and read on load, in the one place every other repair to a definition already happens. Nothing about any existing form changes - a definition with no stamp describes exactly the same shape as one carrying the first version.
* AND A DEFINITION FROM A NEWER VERSION IS LEFT ALONE. If you downgrade the plugin, or import a configuration exported from a site running a newer one, the definitions it does not recognise are read as best it can and never rewritten. Quietly dropping the settings a later version needs would turn a temporary downgrade into permanent data loss.
* The configuration export now writes definitions in the same shape an import will store them in, so a download-and-restore round trip changes nothing at all.

= 1.8.54 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.53 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.52 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.51 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.50 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.49 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.48 =
* A YES/NO COLUMN DRAWN AS A SWITCH IS THE SIZE OF A SWITCH AGAIN, instead of a full-width bar with the knob stranded at the left end. It shared a style rule written for the consent row - which is a label containing a checkbox and a sentence, and so is rightly full width. The consent switch was never affected, because there the switch is nested one level deeper; that is why one switch on a form looked right and the other did not.
* A FIELD THE LAYOUT DOES NOT MENTION now renders in a section card below the designed grid instead of bare and full page width underneath it. A field ticked after the layout was arranged belongs to no section - it is still rendered, it just no longer looks like debris. A form with no layout at all keeps the plain run of fields it has always had.

= 1.8.47 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.46 =
* A VALIDATION MESSAGE NO LONGER SAYS THE FIELD'S NAME TWICE. A phone number that would not validate came back as `"Business Phone": Business Phone must be a valid phone number.` - the wrapper added the label, and the message the validator wrote already began with it. The label is now added only when the message does not already start with it, so a message from somewhere else still gets named and ours does not get named twice. The same sentence is rendered by the add-on's submission handler, which now calls this rule instead of carrying a second copy of it.
* AN ANONYMOUS RECORD SEARCH IS BOUNDED BY WHAT IT HANDS OVER, NOT ONLY BY HOW OFTEN IT IS ASKED. A lookup field with no view assigned searches its whole target table, and on a published form that search is reachable by anyone. The per-request rate limit did not close it: sixty requests a minute, each answering with twenty records, is a table walked with sixty guessed search terms rather than one. Records served to an anonymous visitor by an UNBOUNDED search are now metered per address - 400 an hour by default, well past any real session - and the picker says so when the budget is spent instead of looking like an empty table. A lookup with a view assigned is already bounded by that view and is never metered, so browsing one is unchanged. Filter: cordex_lookup_record_budget.
* The record picker that shows it belongs to the add-on; the meter itself lives here, so both plugins count against one budget.

= 1.8.45 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.44 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.43 =
* A datetime field now records the time the visitor meant. The browser sends its own timezone with the submission, so "09:00" is nine o'clock where the person filling the form is rather than where the site is. A visitor with JavaScript off is unaffected.

= 1.8.42 =
* A new --cordex-settings-max measure, for admin screens whose values are identifiers rather than prose. --cordex-content-max is a reading measure - about ninety characters, the width a paragraph is comfortable at - and it is the right default. It is the wrong cap for a form displaying a 36-character GUID or a full hostname, and being a fixed pixel count it never widened on a larger monitor. The new token is wide enough for those values, still bounded so a settings form does not sprawl, and it grows with the window in between.

= 1.8.41 =
* The "Enable auto-updates" control no longer disappears from the plugin row after an update. WordPress offers that control only for a plugin listed in the update transient's response or no_update list - WP_Plugins_List_Table sets 'update-supported' from precisely that - and core fills those lists from the Update URI filter only AFTER it contacts the API, returning early while its timeout window is open. Installing an update then deletes the transient in wp_clean_plugins_cache(), which runs AFTER upgrader_process_complete, so the update hook cannot usefully write the entry either. Between those two facts sat a window, after every manual update, where the plugin was in neither list and the control silently vanished. It healed itself on the next unthrottled check, which is exactly what made it read as a lost setting rather than a timing artefact. The plugin is now guaranteed to be listed whenever the transient is READ - no network call, no write, no dependence on when core last checked.
* Strictly additive, and tested as such. A pending update under response is left exactly as core wrote it, and so is an existing no_update entry; only a genuine absence is filled, with the installed version and an empty package. Overwriting a real pending update with "up to date" would hide an available release, which would be a considerably worse fault than the one being fixed.
* The test asserts the filter is REGISTERED as well as correct. The four behaviour cases call the method directly and would all still pass with it never hooked up - a right answer nobody asks for. Removing the registration now fails the suite.

= 1.8.40 =
* NO VALIDATION ON EMAIL, PHONE AND WEB ADDRESS on forms built before that validation existed - and the validator was never the problem. A form definition is a SNAPSHOT: the server resolves every field from live metadata at save time and persists it, so a public form costs no metadata call per visitor. The price is that a form saved before a field gained a property has a definition without it, and nothing ever goes back for it. Format-aware validation reads the field's String Format (Email / Url / Phone), so every form built before that property existed handed the check an empty format, the check concluded "plain text", and it accepted anything. Required kept working, because required WAS in the snapshot - which is why the form refused an empty Topic and in the same breath accepted "fhjfhfhh" as an email address and "fghjfghfghjfghfghf" as a website. Re-saving the form in the builder would have fixed it; nobody should have to know that. A stored definition now has its missing formats filled in from live metadata as it is read.
* The repair is applied in ONE place - on the way out of the definition getter - because all four consumers go through it: this plugin's renderer and submit handler, and the add-on's two. The same property has now gone missing in three separate places, each time because a second copy of a read did not know about it, and four call sites would have been four chances to miss the fourth. A definition that is already complete costs nothing, and a metadata read that fails leaves the definition exactly as it was rather than weakening the form.
* A DISABLED submit button now looks and behaves disabled. Holding it closed until required consent is given was only half the job: it kept its full-strength fill and still brightened under the pointer, so a button that could not be used was indistinguishable from one that could - reported as it appearing active, with a hover effect, while the consent switch was off. The rule is emitted whatever the button style setting says; the previous treatment lived inside the branch that only runs when a style is chosen, so a themed button had no disabled state at all.
* The non-production banner has space above it as well as below. It carried a bottom margin only, which is fine when it opens a component - but a page with more than one component put the banner directly beneath the previous one's message bar, flush against it, the two reading as a single block.
* The fixture that measures the button's appearance uses the stylesheet the plugin actually emits, not a copy of the rule under test written into the fixture. The first version inlined the expected CSS by hand, which would have passed whether or not the plugin emitted anything at all - a test that reports a fix as present while the shipped stylesheet is empty is worse than no test. It also no longer borrows another screen's scoped CSS as a fallback: those rules are scoped to a form's dom id and match nothing on a different form, which reported a styled control as unstyled.

= 1.8.39 =
* THE SUBMIT BUTTON STOPPED WORKING AFTER THE FIRST SUCCESSFUL SUBMISSION, and stayed that way. The button is disabled the moment a submit starts, so a request cannot be fired twice. It was re-enabled when the server refused, and re-enabled when the network failed - but not when the submission SUCCEEDED. So the first submission that worked left the button disabled for good: the record was created, the form reset itself, and from then on every click did nothing at all. No event, no reload, no message, no request. Editing the fields afterwards cleared the success notice as well (that is what the notice is meant to do), removing the last trace that anything had happened - so the page looked untouched and the button looked alive. Reported as "I click button and nothing" and, entirely reasonably, as validation being broken: nothing was reaching the server to be validated a second time. The button is now restored as the FIRST statement of the response handler, ahead of every branch, which is how the record page has always done it and why its Save button never stuck.
* That rule is now enforced across both plugins rather than fixed in one place. The disable-around-a-request pattern appears in five call sites across the two add-ons, and it has exactly one failure mode: a re-enable that sits inside a branch is a re-enable that some outcome skips. tools/button-restore-check.js walks every one of them and requires the restore to come before any branch. Where it cannot walk a promise chain to the end it says so by name rather than reporting a pass over ground it never covered.
* Nothing caught this because every fixture in the repository submitted ONCE. A one-shot test cannot see a control that only breaks after it has been used, and "it works the first time" is the most misleading kind of green there is. The harness now submits, succeeds, and submits again.
* The submit button is held closed until required consent is given, and opens the moment it is ticked. A required consent switch that is off is the one thing on the form that can stop a submission while every visible field looks correct; leaving the button live meant the only feedback was a round trip and a sentence at the bottom of the page. Checked in both directions, because a button that is always disabled would pass a one-sided test and be a worse fault than the one it replaces.
* The message bar is scrolled into view when it is shown, and only when it is actually out of view. On a long form the button is the last thing on the screen and the bar renders past the bottom edge, so an answer could be delivered to a place nobody was looking. This was NOT the cause of the dead button above - it was found while looking for it - but it is a real way for a reply to go unseen.
* Buttons follow their own text size. The button was a fixed 32px box with 14px of side padding and line-height 1 - correct at 14px type and wrong at every other size, because nothing about the box moved when the type did. Raise the type and the words crowd the edges and the descenders run at the bottom rule. Padding and the height floor are expressed in em now, so a button keeps its proportions at any size; the numbers are the old ones divided by 14, so a button at the default size is pixel-identical to before.
* The consent switch can be reached with the keyboard. Turning the checkbox into a switch hid the real input at zero width and zero height, which takes it out of the tab order and leaves a browser unable to point a validation message at it. It now carries the switch's own 44x24 footprint at zero opacity: invisible, still focusable.

= 1.8.38 =
* Fixed, from 1.8.37: the designer's numbered steps rendered as a tall stack of full-width rows instead of a row of tabs across the top.

= 1.8.37 =
* Email, phone and website fields are now checked - as you type, and again on the server. Dataverse records what a text column is FOR in its format, and the plugin was not reading it, so nonsense went into a phone or an email column and was accepted. Both are checked now, and a form's inputs use the right keyboard on a phone.
* The check is deliberately not strict about style: "+61 (2) 9876 5432", "555-0158 ext 12" and a bare "example.com" are all accepted. It rejects what cannot be a number or an address, not what is written unusually.
* Text length limits from Dataverse are applied to single-line text columns, which had been read as unlimited.
* Fixed, from 1.8.36: the designer's step navigation scrolled sideways instead of wrapping, and did not go back to a vertical list on a narrower window.
* Fixed, from 1.8.36: the line under the Shortcodes header stopped halfway across its card.

* The Shortcodes screen is balanced: on a wide monitor the designer now has a margin on its right matching the menu on its left, instead of running to the edge of the window. On narrower screens the full width still goes to the designer.
= 1.8.36 =
* The form designer now uses the width of your screen. The layout designer rendered at the same 620px whether your monitor was 1600px or 2560px wide, which left about 48px for each section's title - too narrow to read what you had typed. It now takes roughly two-thirds to four-fifths of the window, and a section title box is between 220px and 533px depending on your screen.
* A section card's title box drops the buttons beside it onto their own line when the column is narrow, instead of being squeezed by them.
* Reading widths are unchanged. Prose, cards and text inputs still stop at a comfortable measure rather than following the monitor - only the working surfaces you arrange things on have been widened.

= 1.8.35 =
* Fixed: with Cordex Forms Pro installed, a published form was capped at 980px and its columns squeezed into part of the width the page gave it. Without the add-on the same form was full width. Admin styling was reaching published pages on add-on sites; it no longer does, and the form takes the width your theme gives it either way.
* Security: cached Microsoft access tokens are now encrypted in the database. Your client secret was always encrypted; the token minted from it was not, so anyone reading the database had a usable credential for up to an hour. Reported by an external security review.
* Fixed: changing a client secret left the old access token in the database until it expired. The flush that was meant to remove it had been deleting the wrong entry, and never removed anything.
* A choice column whose options cannot be read is now refused when you save the form, and a submission against one is refused with a clear reason, instead of being sent to Dataverse to fail there.
* The "sodium extension required" notice is shown only to users who can actually install one.
* Uninstall removes one further option that could previously survive on a fallback path.

= 1.8.34 =
* Fixed: saving a form failed with "Unknown field type." against the text-size rows added in 1.8.33. The control rendered and accepted a value, and the server then refused it. Anyone on 1.8.33 should update.
* The consent control now follows the form's own Yes/No setting: a form whose two-option fields render as Dataverse switches gets a switch on its consent row instead of a lone square checkbox. What is submitted does not change in either style.
* The consent control is centred on its wording rather than pinned to the first line of it, which matters because a consent sentence usually wraps.
* Fixed 35 accessibility warnings on the form designer: every control now carries an id, and each row's label is a real label element wired to its control, so label text is clickable and screen readers no longer announce the screen as unlabelled inputs.
* Export and import now carry a form's rendered appearance, not only the values typed into the designer. Those are two separate records and only the second decides what a visitor sees.

= 1.8.33 =
* New: label text size and field text size in the form designer, as a number and a unit (px, %, em, rem, pt, vw, vh). Both blank by default, meaning inherit your theme. Both travel with a saved style theme.
* Fixed: the consent checkbox rendered at the browser default size and its wrapping sentence ran back underneath it.

= 1.8.32 =
* Fixed a silent data-loss path: a number such as 1e999 passed validation, became infinity, and could not be encoded - so the submission was refused while the visitor was still shown the success message, and the recorded error named the wrong cause. Non-finite numbers are now refused at the field, where they can still be corrected.
* Fixed: deleting a saved shortcode reloaded the screen whether or not the delete had succeeded, so an expired nonce looked exactly like success until the row came back.
* Admin screens no longer hang silently. A server error, a proxy error page or an empty response left screens on "Saving..." indefinitely; the status and body are now always carried through, so "the server said no" is distinguishable from "the server never answered".
* The yes/no switch has a visible keyboard focus ring (WCAG 2.4.7). The real checkbox is transparent so the switch can be drawn, which had hidden the browser's own focus ring with it.

= 1.8.31 =
* Fixed: the saved-shortcodes card was far wider than the cards beside it. Admin width is now structural rather than per-element, so anything added later inherits a sensible width instead of needing one remembered for it.

= 1.8.30 =
* The form designer takes a wider working width - a four-column field picker, a three-column layout designer and a table of metadata targets are a working surface, not prose.
* The preview has its own titled card, hidden until there is something in it, instead of floating loose beneath the other cards.
* Fixed: the preview explanation printed twice on the same screen.

Earlier releases predate this plugin's submission to the WordPress Plugin Directory. The full history ships with the plugin.
