Skip to content

Hot patch 6.14.3#1654

Open
jakejackson1 wants to merge 21 commits into
hot-patch-6.14.2from
hot-patch-6.14.3
Open

Hot patch 6.14.3#1654
jakejackson1 wants to merge 21 commits into
hot-patch-6.14.2from
hot-patch-6.14.3

Conversation

@jakejackson1
Copy link
Copy Markdown
Member

@jakejackson1 jakejackson1 commented May 17, 2026

Description

Branch for the next point release of Gravity PDF.

6.14.3

  • 🧹 Housekeeping: WordPress 7.0 Compatibility
  • 🧹 Housekeeping: Improved screen-reader support for admin pages
  • 🧹 Housekeeping: Remove obsolete Gravity Forms cache workaround
  • 🐞 Bug: Honor PDF Conditional Logic toggle setting, so disabling it in the UI skips stale conditional rules
  • 🐞 Bug: Fix Rich Text Editor rendering error when switching PDF templates
  • 🐞 Bug: Preserve unsaved edits to the Template fields if switching to a PDF template with the same fields (eg. Core / Invoice / Certificate)
  • 🐞 Bug: Preserve the selected template when adding a new PDF
  • 🐞 Bug: Fix Gravity Forms 2.6+ version detection on PDF form-settings pages
  • 🐞 Bug: Fix regression in the Template Manager when an invalid .zip is uploaded
  • 🐞 Bug: Minor UI fixes in the Template Manager
  • 🐞 Bug: Fix regression in the Core Fonts installer when a download error occurs
  • 🐞 Bug: Reset Gravity Wiz Nested Form field IDs after each group in PDFs

@codecov
Copy link
Copy Markdown

codecov Bot commented May 17, 2026

Codecov Report

❌ Patch coverage is 83.11688% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.02%. Comparing base (a7b782e) to head (aa73a5b).

Files with missing lines Patch % Lines
src/View/html/GravityForms/settings_field.php 84.37% 5 Missing ⚠️
src/Model/Model_Form_Settings.php 50.00% 2 Missing ⚠️
src/Helper/Helper_Form.php 0.00% 1 Missing ⚠️
src/Model/Model_Templates.php 50.00% 1 Missing ⚠️
src/View/html/Settings/general.php 0.00% 1 Missing ⚠️
src/View/html/Settings/tools.php 0.00% 1 Missing ⚠️
src/assets/js/react/sagas/templates.js 75.00% 1 Missing ⚠️
src/bootstrap.php 75.00% 1 Missing ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##           hot-patch-6.14.2    #1654      +/-   ##
====================================================
+ Coverage             75.96%   76.02%   +0.05%     
====================================================
  Files                   256      257       +1     
  Lines                 13339    13375      +36     
  Branches                365      370       +5     
====================================================
+ Hits                  10133    10168      +35     
- Misses                 3201     3202       +1     
  Partials                  5        5              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

jakejackson1 and others added 20 commits May 19, 2026 12:11
Preserve the chosen template across the new-PDF POST flow in
Model_Form_Settings before the row is saved, reset the nested-form
field id after each iteration in Field_Form so subsequent passes
start from a clean state, wrap radio/multicheck/conditional_logic/
paper_size inputs in a fieldset/legend in the shared settings_field
markup, add the missing gforms_edit_form class to the general and
tools form wrappers, and apply the matching SCSS tweaks for
WordPress 7.0 / PHP 8.5 compatibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hide the select-default-font tick in the AddUpdateFontFooter when the
Font Manager is opened from the global Tools tab, matching the existing
handleDisableSelectFields() pattern in FontListItems.js — the tick is
meaningless without a parent font dropdown to write the selection back
into. Also pin font-size: 20px / line-height: 1 on the dashicons trash
and tick buttons inside .select-delete-icons-container so they render
at ~35px and match the adjacent .gfpdf-button on WordPress 7.0, where
the inherited <button> line-height (~33px) was ballooning them to 48px.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revert the search result list selector from ol back to ul (the React
component renders <ul class="search-menu">, so the ol rule was an
orphan and stripped row backgrounds, shadows and the flex layout) and
re-add the .hit-action rules that were removed in e35add7. Use
visibility: hidden / visible instead of display: none / flex on
.hit-action so the select-icon slot is always reserved — hovering a
result no longer pushes the title and path onto a new line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add .button to the #gfpdf-template-container and #gfpdf-font-manager-container
override so the activate <a class="button"> picks up the same
min-height: 32px / line-height: 2.30769231 reset as the <button>
elements (without it the broad #tab_PDF .button rule was applying
line-height: 2.95, which rendered as 42.48px in a 36px button).

Set box-sizing: border-box on .theme .more-details so the WP-core
20% / 60% / 20% positioning includes the 24px horizontal padding —
otherwise the hover label sat ~12px right of centre.
…load

apiPostTemplateUploadProcessing never throws on HTTP failures, so the
saga always dispatched the success action — even when the server
returned 400 or a JSON error body. ajaxSuccess then ran
response.body.templates.forEach() on a non-array (e.g. the integer 400
parsed from the wp_die response), throwing a TypeError that unmounted
the modal. ajaxFailed never ran, and even if it had it was reading
error.response.body.error — a shape that no caller produced.

Route the saga to templateUploadProcessingFailed when response.ok is
false or response.body.templates is missing, and rewrite ajaxFailed to
read the new response shape (falling back to genericUploadErrorText
when the body is not a JSON error payload). Tests updated to cover the
three failure paths: non-ok status, missing templates array, and a
thrown fetch.
glob() intermittently returned an empty result immediately after
unzip_file() wrote the new entries through WP_Filesystem, even when
scandir() on the same directory showed the .php files were present.
This surfaced for users as "No valid PDF template found in Zip archive."
on otherwise-valid template uploads, and as a ~6% flake rate on
Test_Templates::test_unzip_and_verify_templates in the suite.

Switch to scandir() + suffix filter for the post-unzip listing so the
directory is always re-read from disk, and harden the test itself with
a unique archive name per run, a flush of the template transient cache
before/after the test, and a try/finally to guarantee cleanup runs even
when an assertion mid-test fails.

Verified 40/40 consecutive full-suite passes after the change.
Replace the scandir + array_filter dance with a CallbackFilterIterator
over a FilesystemIterator, extracted into a small find_template_files()
helper, and reuse it from both the validation path and the post-copy
header lookup so they share one source of truth.

Kept non-recursive to mirror Helper_Templates::get_all_templates_in_folder():
the rest of the template system only discovers top-level .php files in
the install folder, so picking up templates from subdirectories at
validation time would let zips pass that the system can't actually
load post-install.
…im comments

Replace the new Model_Templates::find_template_files() with a direct
call to the existing Helper_Templates::get_all_templates_in_folder(),
which already wraps the same FilesystemIterator + CallbackFilterIterator
pipeline behind a logged try/catch — removes ~30 lines and the
CallbackFilterIterator / FilesystemIterator imports.

Extract the brittle window.location.search.substr(lastIndexOf('=')+1)
pattern used in four Font Manager components into a single
getTabLocation() utility. Preserve the trailing-`=`-value semantics:
the call sites depend on the value of the *final* query parameter
(sometimes 'tab', sometimes 'subview', sometimes the form id), so
URLSearchParams.get('tab') is not a drop-in replacement — it returns
'' on the global PDF settings page (?page=gf_settings&subview=PDF) and
breaks FontListItems.handleSetSelectedFontNameValue.

Drop the redundant pre-test cleanup in test_unzip_and_verify_templates
(the try/finally block already guarantees teardown), and prune
explanatory comments that just restated what the code below them did.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Changing the Template dropdown on a form-level PDF settings page wipes
the user's in-progress edits because the entire template-fields section
is replaced via AJAX with server-rendered markup based on saved values.

Snapshot every input inside the template fieldset before the request
fires, then re-apply matching values to the new HTML before TinyMCE and
wpColorPicker are re-initialised on top of it. Snapshot entries that no
longer match a field in the new template are silently dropped — the new
template doesn't surface them. Restored checkbox/radio toggles dispatch
`change` so dependent conditional panels driven by setupToggledFields
settle into the right state.

The snapshot/restore pair lives in its own formValuesSnapshot module so
it can be unit-tested against a real jQuery + JSDOM environment. Two
non-obvious cases worth flagging:

1. `.gfpdf-input-toggle` checkboxes (the First Page Header / First Page
   Footer toggles) have no `name` attribute, so a named-input scan
   silently drops them. They're snapshotted keyed by the name of the
   textarea inside the container they show/hide — a stable identity
   across HTML swaps — and restored via the same lookup, firing
   `change` so setupToggledFields slides the container correctly.

2. When a TinyMCE editor is in Code/Text mode the editor body has stale
   content and the textarea holds the live edits. The snapshot checks
   `editor.hidden` and reads the textarea in Code mode.

Also fixes a real bug surfaced by the test suite: jQuery's `:button`
pseudo doesn't match input[type=submit] or input[type=reset], so the
guard spells out `:button, :submit, :reset, [type=file]`.

20 Jest tests cover text/number/select/textarea/radio/checkbox, TinyMCE
Visual + Code modes, nameless toggle round-trip, template dropdown
exclusion, button/submit/reset/file skipping, and a full snapshot →
swap → restore integration case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The superagent → fetch migration in 890c053 swapped a client that
auto-rejected on 4xx/5xx for native fetch, which only sets
response.ok = false. The coreFonts saga still only guarded against a
falsy parsed body — and getJsonString('{}') returns a truthy empty
object, so a 400 from /wp-admin/admin-ajax.php was happily treated as
"download succeeded".

The E2E "Tools tab - Install core fonts field test failure" expected
the .gfpdf-core-font-status-error element to render after the mock
returned 400; it never appeared because the saga walked the success
branch.

Add response.ok to the guards on both saga branches (mirrors the
existing pattern in sagas/fontManager.js and sagas/templates.js) and
extend the Jest suite to lock in the non-2xx-is-a-failure behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… round-trip

Add a success-path case for the templateUploadProcessing saga (only the
two failure branches and the thrown-error case were exercised before),
and a round-trip test that snapshots and restores every standard HTML
form input type — including multi-select, multi-checkbox groups, and
hidden/range/date/time/colour inputs — to lock in formValuesSnapshot's
coverage of the field surface it has to handle on a template swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rules

The form-settings UI exposes two related fields — the `conditional` toggle
(user-visible on/off switch) and the `conditionalLogic` rules array — and
they can drift out of sync. When that happens (toggle off, rules array
still populated from a previous enable), the runtime checks evaluated the
stale rules and rejected entries against rules the UI said were disabled,
manifesting as a "[gravitypdf] shortcode says conditional logic enabled,
entry didn't pass" error on form confirmations.

Route all four PDF-level runtime sites — get_pdf_config, middle_conditional,
get_active_pdfs, and the [gravitypdf] merge-tag check — through a new
Helper_Misc::conditional_logic_passes() that treats the toggle as the
source of truth and short-circuits when it's explicitly off. Legacy
settings without the toggle key fall back to evaluating the rules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…plate swap

Switching the PDF template dropdown with the user's last-selected editor
tab set to 'html' was buggy in two ways:

1. switchEditors.go('html') was called immediately after tinyMCE.init()
   while the iframe was still being mounted, throwing
   "Cannot read properties of undefined (reading 'getSelection')" inside
   editor.hide(). The exception unwound the surrounding ajaxCall()
   success callback, skipping initialiseCommonElements, merge tags, and
   tooltip re-init for the swapped-in fields.

2. switchEditors.go reads the iframe selection and, if a range is set,
   schedules textArea.focus() 100ms later (base.js
   selectTextInTextArea), which scrolls the page to the editor. TinyMCE
   seeds a default cursor on init, so this fires for every editor on
   the form — the viewport ends up on the last one, yanking the user
   away from the template dropdown they just clicked.

Defer the switchEditors.go call via editor.on('init', ...) when the
editor isn't initialised yet, then clear the iframe selection inside
the deferred apply() so findBookmarkedPosition short-circuits and the
focus/scroll path is skipped. Wrap the call in a try/catch as a final
safety net: if WP can't switch, the editor falls back to Visual mode
silently and the user can flip the tab manually.

Drive-bys from the same area:
- hoist getUserSetting('editor') out of the editors.forEach loop and
  drop narrative comments inside loadTinyMCEEditor
- replace stringly-typed class_exists() with
  GF_Background_Process::class in bootstrap.php
- reorder/dedupe the 6.14.3 changelog entries

13 Jest tests cover the apply() behaviour — selection-clear, deferred
init via editor.on('init'), missing-getWin/getSelection guards,
switchEditors throwing, switchEditors absent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant