== Changelog ==

The full release history. readme.txt carries only the two most recent releases,
to stay under the wp.org readme size limit; everything else lives here.

= 1.6.5 =
* Removed the one-time migrations that retired the licensing providers Sigilet no longer uses. Freemius (retired at 1.6.0) and Appsero (removed at 1.6.2) both left records behind on installs that ran them, and a premium-only migration cleaned those up on upgrade. No install that can still be reached needs it: Appsero was never configured in any shipped build, so no site could hold its records, and the remaining Freemius installs are being moved onto Sigilet's own licensing service directly rather than migrated. Sites installed from WordPress.org never carried either migration and are unaffected.

= 1.6.4 =
* Internal clarity only, no functional change: the base64 @font-face rule embedded in preset badge SVGs moved out of a PHP string and into the SVG asset file assets/presets/inter-font-face.svg, which carries a {{fontdata}} token like the other preset templates. The rule is SVG-document markup spliced into a preset before Imagick/librsvg rasterizes it to PNG bytes - no browser ever receives it - so it has no wp_enqueue_style() path. Enqueuing it would move the @font-face into the WordPress page, where the rasterizer cannot see it, and preset badges would render in a substituted font. The 1.6.3 rename and docblock were not enough to stop the markup reading like page output when grepped; an asset file is.

= 1.6.3 =
* Internal clarity only, no functional change: renamed the private helper that builds the base64 @font-face block embedded in preset badge SVGs, and documented that its output is spliced into an SVG document for the image rasterizer rather than sent to a browser. The old name read like web-page output and invited the misreading that the style block should be enqueued; enqueuing it would move the @font-face into the page, where the rasterizer cannot see it, and preset badges would render in a substituted font.

= 1.6.2 =
* Pro licensing moves from Appsero to Sigilet's own licensing service. Entering a license key claims a seat for the site and stores a signed entitlement token that is verified locally, so Pro features keep working through a service outage - a customer loses Pro only when the service explicitly says the license is no longer valid for that site, never on a timeout or an unreachable server.
* Pro updates come from the same service. One request returns the update decision, the "View details" content and a short-lived signed download, and the download is checked against a SHA-256 digest before WordPress installs it.
* Removed the Appsero client, its vendored updater and the licensing adapter entirely. Appsero was never configured in a shipped build - no activation against it could ever have succeeded - so no install loses anything. Any leftover option or daily event from a development install is retired on upgrade.
* Polar becomes the merchant of record for Pro purchases, replacing Paddle. Payment and billing data stay outside WordPress either way; the plugin embeds neither and calls neither.
* The Pro build no longer accepts a WordPress.org update for its own slug. Both builds share the slug `sigilet` so that Pro is an in-place replacement rather than a second plugin, which means WordPress keeps offering the free package once its version number overtakes the installed Pro one - and installing it would silently delete every Pro file. The Pro build now refuses that offer in every state, including when it cannot reach the licensing service, and answers "View details" itself so the free listing is never shown in its place.
* The WordPress.org build is unchanged: it still ships no licensing client, no public key, no License screen and no payment-provider claim, and still updates only from WordPress.org.

= 1.6.1 =
* The WordPress.org build no longer carries the one-time migration that retires leftover Freemius records. Only an install that actually ran the Freemius SDK can hold those records, which means the premium package alone, so the migration moved there and the free build no longer writes an option belonging to another plugin. Premium upgrades from 1.5.x are unaffected and still clean up on first admin load.
* Added a `sglt_upgrade` action, fired by the upgrade routine before the stored version advances, so the premium package can run its own version-gated migrations.

= 1.6.0 =
* Replaced Freemius with Appsero licensing and authenticated premium updates. The integration uses a Sigilet-owned license screen, a namespace-isolated and pinned Appsero client/updater, no Appsero Insights, and a seven-day cached-entitlement grace period for temporary transport failures.
* Paddle becomes the merchant of record for new Pro purchases; payment and billing data remain outside WordPress.
* The free build strips the complete Appsero client/updater directory and relies exclusively on WordPress.org updates. Pro customers install the premium package supplied after checkout.
* Removed the Freemius bootstrap, vendored SDK, conditional menu workarounds, opt-in analytics, uninstall event, packaging transforms, and SDK-specific test matrix.

= 1.5.11 =
* Fixed the Badges menu having no **License** item at all on a site that skipped the opt-in. The item was shown only while the plugin was waiting for a license or an opt-in, and Freemius supplies its own only once a site is registered - so a skipped opt-in fell between the two and left licensing reachable from the Plugins screen alone. Whether to add the item is now decided by what is actually registered in the menu rather than by predicting it from the install's state.

= 1.5.10 =
* The Account screen is now called **License**, and a License item stays in the Badges menu even before a license has been activated - previously it appeared only once licensed, which is the opposite of when you need it. Until then licensing was reachable only from the Plugins screen, which is the last place anyone looks.
* Fixed "Upgrade to Pro" on the bulk-award screen answering "Sorry, you are not allowed to access this page." on a site that had not opted in or activated a license. Same cause as the 1.5.9 fix, on the pricing screen rather than the Account one; it affected free installs that had not opted in as well as unlicensed Pro installs.

= 1.5.9 =
* Fixed a Pro install that has not had its license activated yet being locked out of its own licensing screens. The Account, License and opt-in pages all answered "Sorry, you are not allowed to access this page.", so there was no way from the admin to activate a license or opt in. Free sites and already-licensed sites were never affected.
* Every link labelled "License" or "Account" now goes to whichever licensing screen the site can actually open, instead of always assuming the Account page exists.

= 1.5.8 =
* Made corrections to text across interface copy, code comments, and documentation by replacing em and en dashes with plain punctuation.
* The "Verified by Sigilet" link in the verification page footer is now **off by default** and opt-in under Settings → Verification page.
* The "Add to LinkedIn" button setting now has a control. It was documented and read by the verification page, but had no checkbox on the Settings screen, so it could not actually be switched on.
* Saving a badge no longer re-orders the saved-contacts list unless you actually changed the contact. Re-saving an old badge used to promote its contact back to the top and make it the default for the next badge.

= 1.5.7 =
* Neutral example text on the badge and award forms. The recipient field suggested "volunteer@example.org" and the badge builder suggested a volunteering title and criteria; these now read as generic training examples, so the placeholders no longer imply a sector.

= 1.5.6 =
* Every Sigilet admin screen now carries the plugin's logo and name at the top, and a link to sigilet.com at the bottom. The logo switches to its light-on-dark version automatically when the screen it sits on has a dark background.
* Badge contacts are remembered. The contact email and name you save on a badge are added to a "Use a saved contact" dropdown, so the next badge - or another issuer on the same site - can pick an existing contact instead of retyping it.
* Add Badge now pre-fills the contact fields with the one you last used, so an issuer who always signs badges the same way cannot forget to set it. Still editable, and clearing both fields falls back to the organisation contact from Settings.
* Saved contacts can be removed under Sigilet → Settings → Saved contacts. Removing one only takes it off the dropdown; badges already using the address keep it.

= 1.5.5 =
* The bulk-award email queue is now Pro-only code, so the free build no longer registers its background worker endpoint - that machinery had nothing to drive it without the CSV importer.
* The email-deliverability notice is now dismissible, with a "Don't show this again" option that sticks per user.
* Internal: corrected phpcs annotations, added missing translator comments, and aligned the plugin name with readme.txt.

= 1.5.4 =
* Fixed the badge builder's "Reuse a previous badge image" gallery offering back preset designs it had generated itself, alongside your own uploads. The gallery now shows only artwork you supplied - the preset designs are still one click away in the row above it. Existing sites are tidied automatically on upgrade; no images are deleted.

= 1.5.3 =
* Revoked assertions now return **410 Gone** at their hosted URL, as Open Badges 2.0 requires. Previously the JSON correctly reported `"revoked": true` but the response was a 200, so a strict verifier could read a revoked badge as still valid.
* Removed the non-standard `uid` property from assertion JSON - it is an Open Badges 1.1 term that v2 validators flag as unmapped.
* Bulk award: the working file holding in-flight recipient addresses is now kept out of the web root even on hosts where WordPress' temp directory resolves inside it, is guarded for Apache, nginx and IIS, and is shredded within an hour of a job stalling rather than after a day.
* Uninstall no longer boots the plugin, now removes every custom capability (previously the primitive caps were left on the administrator role), and cleans up queue options, transients and any leftover working files.
* The Freemius opt-in "Skip" option is now pinned on explicitly rather than relying on the SDK default.
* Hardened input sanitizing on the revoke-reason preset handler and the social-card `If-None-Match` header.
* Removed retired code: the pre-1.5.0 signed-key licensing tools, the superseded site-wide identity-salt helpers, and an unused assertion lookup.

= 1.5.2 =
* A re-clicked opt-in confirmation email link now returns you to Sigilet → Account instead of a "Sorry, you are not allowed to access this page." error. The link is single-use; the first click is unaffected.

= 1.5.1 =
* Fixed the plugin's admin pages returning "Sorry, you are not allowed to access this page." on a newly installed site that had not yet completed the Freemius opt-in. Affected every Badges submenu, including the setup wizard a fresh install is redirected to.

= 1.5.0 =
* Licensing moves to Freemius. Pro is unlocked by a license activated under Sigilet → Account; the old Settings → Licensing tab and its offline signed-key path are removed, and the orphaned license-key option is deleted on upgrade. Badges, verification and single awards are unaffected.

= 1.4.5 =
* The LinkedIn/social share card (`og:image`) now defaults to a **light** style (white background, accent-tinted panel, dark text). Selectable under Settings → Share card style (Light / Dark).

= 1.4.2 =
* Admin UI overhaul for non-technical issuers plus a guided revoke flow. Task-first menu order, plain-language award copy, and a confirmation screen with reusable revoke-reason presets. Revoked badges show "Revoked on <date>" and a greyscale watermark on the verification page.

= 1.4.1 =
* Award-email images now render across every mail transport: hotlinked on publicly reachable hosts (survives API senders that strip inline CID parts), inline CID fallback on local/dev hosts. Badge `<img>` gains meaningful alt text.

= 1.4.0 =
* Bulk-award personalisation and dispatch verification: optional `first_name`/`last_name` CSV columns (transient, never stored), a redacted dispatch log in the completion report, and a nonce-guarded "Cancel remaining emails" control.

= 1.3.0 =
* CSV bulk award (Pro) with a server-side paced email queue. Assertions are created synchronously; emailing runs in a self-chaining loopback worker with a heartbeat fallback (no WP-Cron dependency). Addresses live in a single locked temp file, shredded at job end.

= 1.1.3 =
* LinkedIn feed sharing: a new `/badges/social/{uuid}` route generates a 1200×627 social card served as `og:image`, plus a "Share to LinkedIn feed" button. Cache-busted preview URL.

= 1.0.6 =
* Badge and assertion URLs now use random v4 UUIDs instead of sequential post IDs, so resources are not enumerable. Clean cutover: old integer URLs no longer resolve.

= 1.0.5 =
* Badge baking: the download button serves a baked PNG with the assertion embedded in an Open Badges `iTXt` chunk; non-PNG uploads are converted to PNG then baked when GD supports the source format.

= 1.0.0 =
* Initial release: hosted Open Badges 2.0 issuance, verification page, JSON endpoints, roles/caps, revocation, expiry.
