fix: licensing environment type, inactive license status, and dead update packages (6.17.1) - #1734
Merged
jakejackson1 merged 1 commit intoSep 21, 2026
Conversation
jakejackson1
force-pushed
the
fix/licensing-environment-inactive-package-6.17
branch
2 times, most recently
from
September 17, 2026 03:18
178c5a0 to
7e754cd
Compare
…date packages License and update requests now declare wp_get_environment_type() and an explicit `url`, so the store can exclude non-production sites from activation counts (#1726) and the URL recorded here is the URL the store activated, rather than one inferred from the request's user agent. `inactive` from the store means the key is valid, unexpired and not disabled, but EDD holds zero activated sites for it: every site was removed from the customer's account, or the key was never activated anywhere. Only `check_license` can return it — `activate_license` has its own error set — so it is always a verdict on a key that was already saved. The old message ("not active, check your account or contact support") described none of that and sent people to support; it now mirrors `site_inactive` and points at the fix. The status also counts as unentitled when withdrawing a network-shared package (#1728). The store hands a site_inactive license a package URL that 401s. set_version_info_cache() now blanks `package` and `download_link` for any unentitled status, so the dead URL is never offered and a Multisite site borrows the network package instead (#1730). It keys on UNENTITLED_LICENSE_STATUSES rather than `! is_license_active()`: the core plugin updater never sets a status, and error/rate_limit are not verdicts on the license. It lives in the cache setter because the bulk get_version response writes the other add-ons' caches without going through get_version_from_remote(). plugins_api_filter() read the per-site cache directly, skipping maybe_apply_network_package(). On a per-site Multisite, a site whose own package was blanked had no install/update button in the details modal while its update row borrowed the network package. The filter now goes through get_repo_api_data(), and a borrowed package also sets download_link, which the modal needs to show the button. The scheduled and bulk checks now reconcile the URL the key is activated for. An active license proves the key is activated for home_url(), so that URL is recorded; `inactive` / `site_inactive` against a *different* recorded URL means the site was cloned or moved — production copied to staging — and the key is still activated for the old URL, so it is activated for this one. The same URL on record is left alone: that is a deliberate deactivation from the customer's account, and the message above asks them to resave if it wasn't. A rejected attempt backs off for a week, matching the check cadence. update_license_info() records the URL whenever it writes to the database and the store has confirmed the key, covering the first save, the GPDF_LICENSE_KEY constant path and Access Pass propagation. Without that, a site licensed today had nothing on record for up to a week, and sync_license_activation_url() reads an empty record as "not evidence the URL changed" — so a clone during that window went undetected, the very case the record exists to catch. The settings save cannot use that funnel: it activates with $use_database = false because Helper_Abstract_Options::settings_sanitize() ends with an array_merge() over a snapshot taken before the filter runs, so a direct write during the save is silently reverted. The value is threaded through $input instead, with license_{slug}_url registered as a hidden field so it survives the sanitize whitelist, exactly as _status and _message already are. Whatever is posted for that field is discarded in favour of what is on record: only an activation during this request proves the key is activated here, and a stale valid status on a clone would otherwise overwrite the production URL and hide the move from the check that acts on it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jakejackson1
force-pushed
the
fix/licensing-environment-inactive-package-6.17
branch
from
September 21, 2026 01:49
2eacaea to
8edfe6d
Compare
jakejackson1
deleted the
fix/licensing-environment-inactive-package-6.17
branch
September 21, 2026 04:42
jakejackson1
added a commit
that referenced
this pull request
Sep 21, 2026
Ports the 6.17.1 release (6.17..6.17.1) to development: - keep mPDF's cache folders through tmp cleanup, and keep font metrics for a week (#1731) - stop concurrent PDFs failing with "Temporary files directory is not writable" (#1733) - note a PDF left off a notification on the entry, linked to its settings and authored as the notification, with the Gravity PDF logo as its avatar (#1732, #1736) - log generation errors with form/entry/PDF IDs instead of the whole object (#1732) - licensing environment type, inactive license status and dead update packages (#1734) - tag mPDF's log records with the form, entry and PDF they belong to (#1738) - keep PDF URL paths and safe query args when redacting logs (#1737) - retry license reactivation soon when the store gives no verdict (#1739) Adapted to development: tests moved to tests/phpunit/integration on the shared TestCase, Context_Logger uses the scoped GFPDF_Vendor\Psr\Log, the generation-error log context lives in development's refactored Model_PDF::process_and_save_pdf(), and pdf_id defaults to '' for a Helper_PDF built without a PDF ID. Adds the 6.17.1 changelog section. Left out: the version bump and the wp-env 11 CI change (development fixed the Debian 11 build its own way in #1724). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jakejackson1
added a commit
that referenced
this pull request
Sep 21, 2026
Ports the 6.17.1 release (6.17..6.17.1) to development: - keep mPDF's cache folders through tmp cleanup, and keep font metrics for a week (#1731) - stop concurrent PDFs failing with "Temporary files directory is not writable" (#1733) - note a PDF left off a notification on the entry, linked to its settings and authored as the notification, with the Gravity PDF logo as its avatar (#1732, #1736) - log generation errors with form/entry/PDF IDs instead of the whole object (#1732) - licensing environment type, inactive license status and dead update packages (#1734) - tag mPDF's log records with the form, entry and PDF they belong to (#1738) - keep PDF URL paths and safe query args when redacting logs (#1737) - retry license reactivation soon when the store gives no verdict (#1739) Adapted to development: tests moved to tests/phpunit/integration on the shared TestCase, Context_Logger uses the scoped GFPDF_Vendor\Psr\Log, the generation-error log context lives in development's refactored Model_PDF::process_and_save_pdf(), and pdf_id defaults to '' for a Helper_PDF built without a PDF ID. Adds the 6.17.1 changelog section. Left out: the version bump. Also takes 6.17.1's wp-env upgrade (^11.15.0, which repoints Debian 11 sources at archive.debian.org itself) in place of #1724's tools/wp-env/patch-bullseye-apt.mjs, which the yarn wp-env scripts no longer run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 27, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #1726, resolves #1728, resolves #1730.
License and update requests now send the site's environment type from
wp_get_environment_type(). The store can use it to leave staging, development and local sites out of activation counts.An
inactivelicense status now shows its own message ("This license key is not active. Please check your account or contact support.") instead of the generic unknown error. It also counts as unentitled, so a site that goes inactive withdraws any update package it shared across a Multisite network.When a license key is valid but not activated for the current domain, the store still returns a download URL, but that URL fails with a 401. The URL is now dropped before it is cached. Such a site is no longer offered an update that fails halfway through installing, On a Multisite it borrows the working package another licensed site shared, in both the update row and the plugin details popup.
Try it
The first dump prints your environment type (e.g.
"local"), and the second prints an empty string. Without this change the key is missing and the dead URL is kept.Test plan
environmentin the request body (visible in the debug log){"license":"inactive"}shows the new message on the License tabsite_inactiveshows "Automatic update is unavailable" on a single site instead of an update that failssite_inactivesite installs an update another licensed site sharedMore info
Environment (#1726).
environmentis added to bothHelper_Abstract_Addon::get_default_api_params()(activate, check, deactivate) andEDD_SL_Plugin_Updater::get_version_api_params()(get_version, plus the bulkcheck_licenseinModel_Settings). It falls back toproductionwhenwp_get_environment_type()is unavailable (WP < 5.5), matching EDD's stock updater. api.gravitypdf.com must forward the parameter to EDD for it to take effect.Inactive (#1728). Added to
Helper_Data::addon_license_responses()andEDD_SL_Plugin_Updater::UNENTITLED_LICENSE_STATUSES. Per the issue comment, the store may auto-activate and never returninactive. If it doesn't, these entries are harmless.Dead package (#1730). This deliberately departs from the fix proposed in the issue comment (
! is_license_active()inget_version_from_remote()), for three reasons:gravity-pdf-updater.php) never sets a license status, so! is_license_active()would strip every core update package.errorandrate_limitare not verdicts on the license, and must not throw away a working URL. Keying onUNENTITLED_LICENSE_STATUSESexcludes them, the same reasoning that constant already documents.Model_Settings::licensing_bulk_get_version_api_response()writes the other bundled add-ons' caches straight throughset_version_info_cache(), bypassingget_version_from_remote(). The strip lives in the cache setter so both writers are covered. It edits the response object in place, so the value returned byget_version_from_remote()is cleaned too.maybe_apply_network_package()is unchanged: its emptiness test is now correct. A cache written before a status change is already cleared byflush_update_cache().Details modal.
plugins_api_filter()used to read the per-site cache directly, skippingmaybe_apply_network_package(). A site with no usable package of its own got no install/update button in the modal (WordPress only shows it whendownload_linkis set), even though its update row borrowed the network package. This affected unlicensed sites before this PR too. The filter now callsget_repo_api_data(), removing its own copy of the cache-else-fetch logic, and a borrowed package also setsdownload_link.show_changelog()goes through the same filter, so it is covered as well.Tests. New or extended:
providerUnentitledLicenseStatusgainsinactive,test_set_version_info_cache_drops_unentitled_packageandtest_set_version_info_cache_keeps_package_without_a_license_verdict(data providers), the network borrow test is parameterized with asite_inactivedead-URL case and asserts the modal'sdownload_link,test_get_version_api_params_sends_environment_type,test_license_api_requests_send_environment_type, and aninactiveblock intest_schedule_license_check.Full suite: single-site 1218 tests OK. Multisite has 1 error in
test_is_non_active_multisite, which also fails on the untouched base branch in a full run and passes in isolation (order-dependent, pre-existing). PHPCS is clean.