Skip to content

Add File Upload fields to PDF conditional logic - #1961

Merged
jakejackson1 merged 3 commits into
developmentfrom
fix-1619-file-upload-conditional-logic
Oct 26, 2026
Merged

jakejackson1 merged 3 commits into
developmentfrom
fix-1619-file-upload-conditional-logic

Conversation

@gravitypdf-bot

@gravitypdf-bot gravitypdf-bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

PDF conditional logic had no way to check whether a File Upload field holds a file: Gravity Forms leaves File Upload fields out of its conditional logic field list. This PR adds single and multi-file File Upload fields to the PDF conditional logic UI with "is / is not Empty", and makes an empty multi-file list ([]) count as empty.

  • File Upload fields now appear in the PDF conditional logic field list, with is / is not and an "Empty" value
  • Fields built on the File Upload field, such as Image Hopper, are listed too
  • Multi-file values are compared file by file, so an entry saved as [] is treated as empty
  • PHPUnit coverage for the field list, a File Upload subclass, and single, multi, empty and [] entries
  • Playwright spec tests/playwright/core/pdf-settings/file-upload-conditional-logic.spec.ts for the settings UI, saving and the entry list PDF link

Resolves #1619

Try it

wp eval '
$form_id = GFAPI::add_form( [ "title" => "Issue 1619", "fields" => [ [ "id" => 1, "type" => "fileupload", "label" => "Upload", "multipleFiles" => true ] ] ] );
$pdf_id  = GPDFAPI::add_pdf( $form_id, [ "name" => "Has upload", "template" => "zadani", "filename" => "test", "active" => true, "conditional" => 1, "conditionalLogic" => [ "actionType" => "show", "logicType" => "all", "rules" => [ [ "fieldId" => "1", "operator" => "isnot", "value" => "" ] ] ] ] );
foreach ( [ "", "[]", wp_json_encode( [ "https://example.com/a.pdf" ] ) ] as $value ) {
	$entry_id = GFAPI::add_entry( [ "form_id" => $form_id, "1" => $value ] );
	$url      = GPDFAPI::get_pdf_url( $entry_id, $pdf_id );
	printf( "%-40s %s\n", var_export( $value, true ), is_wp_error( $url ) ? $url->get_error_code() : "PDF available" );
}
'

Expected: '' and '[]' print conditional_logic, the entry with a file prints PDF available. On development the '[]' entry prints PDF available.

Test plan

  • E2E: both File Upload fields are listed with is / is not and an "Empty" value, an "is not Empty" rule survives save and reload, and only the entry with a file gets a "View PDF" link
  • With Image Hopper active, an Image Hopper field is listed in PDF conditional logic
  • Submit a real entry through the form with and without a file and check the PDF shows / hides
  • Repeat with "is Empty" on the single-file field
More info

Root cause. Gravity Forms' admin JS only lists field types from its own conditional logic whitelist, which excludes fileupload (there's nothing to compare on the front end before upload). Gravity PDF already had a mechanism for exactly this case: Helper_Data::get_conditional_logic_options() keeps a map of "post-submission" field types (until now only the Gravity Wiz Unique ID field), which is passed to the admin page as gfpdf_extra_conditional_logic_options and injected through the gform_conditional_logic_fields / gform_conditional_logic_operators / gform_conditional_logic_values_input JS filters in handlePDFConditionalLogic.js. fileupload is added to that map with is/isnot operators and a single Empty ('') choice, so the value input renders as a dropdown instead of a free-text box. Field types whose registered class extends GF_Field_FileUpload (from GF_Fields::get_all()) are also passed to GFAPI::get_fields_by_type() and use the fileupload entry, so Image Hopper's image_hopper / image_hopper_post fields show up (their get_input_type() only says fileupload during submission and a few other requests, not on this screen), and nested fields are still walked. The map entries are now merged in whole, so a type can carry choices (this also removes a PHPStan baseline entry for the old rgars() call). No JS changes were needed.

Evaluation. PDF conditional logic runs through GFCommon::evaluate_conditional_logic(). For a File Upload field the source value is the raw entry value: a URL (single) or a JSON list (multi). '' already worked with is '', but multi-file fields can be saved as "[]" (e.g. get_parsed_list_of_files() when no existing file matches), which a string comparison treats as not empty. Controller_Form_Settings::conditional_logic_set_rule_source_value() (on gform_rule_source_value) now turns File Upload values into a list of file URLs (empty and "[]" give an empty list, other JSON is decoded, a plain URL becomes a one-item list), and Gravity Forms' own array matching handles is '' (every value empty, so an empty list matches) and isnot '' (at least one file). The value is decoded inline rather than with GF_Field_FileUpload::to_array(), which only exists since Gravity Forms 2.10 while the plugin still allows 2.5+. A value that's already an array (from another plugin's gform_rule_source_value callback) is passed through untouched, since json_decode() throws a TypeError on an array under PHP 8 and this filter runs on every Gravity Forms conditional-logic check. As a side benefit the entry-meta option list is no longer built when no entry is passed.

Checks run locally

  • PHPUnit, full single-site integration suite on the live wp-env tests container (own DB): 2054 tests, 0 failures, 54 skipped
  • New tests fail on development's controller (the [] case) and pass with the fix
  • Playwright: the new spec plus pdf-settings.spec.ts and conditional-logic-pdf.spec.ts on a fresh e2e env, all pass. Without the fix the new spec times out selecting the File Upload field (did not find some options)
  • The E2E creates the file entry with an array value: the GF REST API wraps a JSON string in another list, so the [] case is left to PHPUnit
  • PHPCS (tools/phpcs/config.xml and PHP compatibility) and ESLint on the changed files: clean
  • PHPStan (composer phpstan): no errors

The 6.18.0 backport is #1968.

🤖 Generated with AI

Gravity Forms leaves File Upload fields out of its conditional logic field list, since there is nothing to
compare until the entry is saved. PDF conditional logic is always evaluated against a saved entry, so offer
single and multi-file uploads with "is / is not Empty" through the same post-submission field map the
Unique ID perk already uses.

Multi-file uploads are stored as a JSON list, and Gravity Forms can save an empty "[]" (e.g. when every
existing file fails validation), which a plain string comparison treats as not empty. The rule source value
for File Upload fields is now converted to an array of file URLs with GF_Field_FileUpload::to_array(), so
Gravity Forms' own array matching decides emptiness.

The options map now carries choices per field type, which also lets PHPStan drop a baseline entry.

Resolves #1619

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report for commit: 797bee0
File: ./tmp/jest-coverage/clover.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  30% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  40% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  50% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  60% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  70% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.5%
  80% │ ██░░░░░░░░░░░░░░░░░░░░░ │  6.1%
  90% │ ███████░░░░░░░░░░░░░░░░ │ 21.2%
 100% │ ███████████████████████ │ 71.2%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 93.30% | Methods: 88.89% | Branches: 82.27%
FilesLinesMethodsBranches
src/assets/js/react/actions
   coreFonts.js100.00%100.00%100.00%
   fontManager.js100.00%100.00%100.00%
   templates.js100.00%100.00%100.00%
src/assets/js/react/components/Alert
   Alert.js100.00%100.00%100.00%
src/assets/js/react/components/CoreFonts
   CoreFontContainer.js100.00%100.00%91.43%
   CoreFontCounter.js100.00%100.00%100.00%
   CoreFontListResults.js100.00%100.00%85.71%
   CoreFontListSpacer.js100.00%100.00%100.00%
src/assets/js/react/components
   CustomHashRouter.js100.00%100.00%100.00%
   Empty.js100.00%100.00%100.00%
   ShowMessage.js80.65%80.00%68.75%
   Spinner.js100.00%100.00%100.00%
src/assets/js/react/components/FontManager
   AddFont.js100.00%100.00%100.00%
   AddUpdateFontFooter.js85.37%50.00%88.89%
   AdvancedButton.js100.00%100.00%100.00%
   FontList.js100.00%50.00%65.22%
   FontListAlertMessage.js100.00%100.00%100.00%
   FontListHeader.js100.00%100.00%100.00%
   FontListIcon.js100.00%100.00%100.00%
   FontListItems.js85.39%64.00%68.66%
   FontListSkeleton.js100.00%100.00%100.00%
   FontManager.js77.78%57.14%50.00%
   FontManagerBody.js94.20%96.43%90.29%
   FontManagerHeader.js100.00%100.00%100.00%
   FontVariant.js90.00%60.00%70.00%
   FontVariantLabel.js100.00%100.00%100.00%
   InitialAddUpdateState.js100.00%100.00%100.00%
   SearchBox.js90.00%66.67%69.23%
   TemplateTooltip.js100.00%75.00%100.00%
   UpdateFont.js75.00%50.00%75.00%
src/assets/js/react/components/Modal
   CloseDialog.js93.33%66.67%70.59%
src/assets/js/react/components/Template
   TemplateActivateButton.js100.00%100.00%100.00%
   TemplateButton.js85.71%66.67%100.00%
   TemplateContainer.js71.43%66.67%25.00%
   TemplateDeleteButton.js100.00%100.00%70.00%
   TemplateFooterActions.js100.00%100.00%100.00%
   TemplateHeaderNavigation.js82.35%85.71%70.00%
   TemplateHeaderTitle.js100.00%100.00%100.00%
   TemplateList.js100.00%100.00%100.00%
   TemplateListItem.js100.00%100.00%92.86%
   TemplateListItemComponents.js100.00%100.00%100.00%
   TemplateManagerClosed.js100.00%100.00%100.00%
   TemplateScreenshot.js100.00%100.00%100.00%
   TemplateScreenshots.js100.00%100.00%50.00%
   TemplateSearch.js93.75%88.89%50.00%
   TemplateSingle.js100.00%100.00%100.00%
   TemplateSingleComponents.js100.00%100.00%75.00%
   TemplateUploader.js100.00%100.00%94.44%
   TemplateUploaderContext.js100.00%100.00%100.00%
   TemplateUploaderTile.js100.00%100.00%100.00%
src/assets/js/react/reducers
   coreFontReducer.js95.65%100.00%88.00%
   fontManagerReducer.js87.21%75.00%75.00%
   index.js100.00%100.00%100.00%
   templateReducer.js100.00%100.00%100.00%
src/assets/js/react/sagas
   coreFonts.js91.67%100.00%75.00%
   fontManager.js86.96%90.00%83.33%
   index.js100.00%100.00%100.00%
   templates.js90.91%100.00%100.00%
src/assets/js/react/selectors
   getTemplates.js91.11%100.00%83.33%
src/assets/js/react/utilities/FontManager
   adjustFontListHeight.js100.00%100.00%100.00%
   associatedFontManagerSelectBox.js94.44%100.00%66.67%
   fontManagerReducer.js100.00%100.00%100.00%
   getTabLocation.js100.00%100.00%100.00%
   toggleUpdateFont.js100.00%100.00%100.00%
src/assets/js/react/utilities/Template
   hasPendingUpload.js100.00%100.00%100.00%
src/assets/js/react/utilities
   withRouterHooks.js100.00%100.00%100.00%

🤖 Jest coverage report

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report for commit: 797bee0
File: tmp/coverage/report-xml/merged.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ██████░░░░░░░░░░░░░░░░░ │  8.8%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.5%
  30% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.4%
  40% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.5%
  50% │ ██████░░░░░░░░░░░░░░░░░ │  8.8%
  60% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.4%
  70% │ ██░░░░░░░░░░░░░░░░░░░░░ │  2.3%
  80% │ ████████████░░░░░░░░░░░ │ 17.5%
  90% │ ███████████████████████ │ 34.6%
 100% │ █████████████████░░░░░░ │ 24.4%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 88.31% | Methods: 95.13%
FilesLinesMethodsBranches
/var/www/html/wp-content/plugins/gravity-pdf
   api.php98.06%100.00%100.00%
   gravity-pdf-updater.php53.97%100.00%100.00%
   pdf.php59.82%81.25%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Controller
   Controller_Actions.php99.07%100.00%100.00%
   Controller_Activation.php95.65%100.00%100.00%
   Controller_Custom_Fonts.php94.04%100.00%100.00%
   Controller_Debug.php100.00%100.00%100.00%
   Controller_Export_Entries.php96.77%100.00%100.00%
   Controller_Form_Settings.php87.37%90.00%100.00%
   Controller_Install.php100.00%100.00%100.00%
   Controller_Mergetags.php100.00%100.00%100.00%
   Controller_PDF.php81.46%100.00%100.00%
   Controller_Pdf_Cache.php100.00%100.00%100.00%
   Controller_Pdf_Queue.php85.27%87.50%100.00%
   Controller_Save_Core_Fonts.php88.76%100.00%100.00%
   Controller_Settings.php86.90%100.00%100.00%
   Controller_Shortcodes.php100.00%100.00%100.00%
   Controller_System_Report.php100.00%100.00%100.00%
   Controller_Templates.php100.00%100.00%100.00%
   Controller_Uninstaller.php83.33%77.78%100.00%
   Controller_Upgrade_Routines.php93.75%100.00%100.00%
   Controller_Webhooks.php100.00%100.00%100.00%
   Controller_Zapier.php100.00%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Exceptions
   GravityPdfDatabaseUpdateException.php50.00%100.00%100.00%
   GravityPdfDomainException.php50.00%100.00%100.00%
   GravityPdfException.php50.00%100.00%100.00%
   GravityPdfFontNotFoundException.php50.00%100.00%100.00%
   GravityPdfIdException.php50.00%100.00%100.00%
   GravityPdfModelNotUpdatedException.php50.00%100.00%100.00%
   GravityPdfRuntimeException.php50.00%100.00%100.00%
   GravityPdfShortcodeEntryIdException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfConditionalLogicFailedException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfConfigNotFoundException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfInactiveException.php50.00%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Fields
   Field_Address.php92.86%100.00%100.00%
   Field_Chainedselect.php66.67%75.00%100.00%
   Field_Checkbox.php94.44%100.00%100.00%
   Field_Consent.php94.74%100.00%100.00%
   Field_Coupon.php--100.00%
   Field_Creditcard.php83.33%100.00%100.00%
   Field_Date.php83.33%100.00%100.00%
   Field_Default.php83.33%100.00%100.00%
   Field_Discount.php44.00%75.00%100.00%
   Field_Email.php83.33%100.00%100.00%
   Field_Fg_Ls_Consent.php92.86%100.00%100.00%
   Field_Fg_Ls_Signature.php73.08%66.67%100.00%
   Field_Fileupload.php95.83%100.00%100.00%
   Field_Form.php90.91%100.00%100.00%
   Field_Hidden.php81.82%100.00%100.00%
   Field_Html.php89.47%100.00%100.00%
   Field_Image_Choice.php86.67%100.00%100.00%
   Field_Likert.php97.22%100.00%100.00%
   Field_List.php92.41%100.00%100.00%
   Field_Multi_Choice.php50.00%100.00%100.00%
   Field_Multiselect.php92.59%100.00%100.00%
   Field_Name.php84.62%100.00%100.00%
   Field_Number.php83.33%100.00%100.00%
   Field_Option.php96.15%100.00%100.00%
   Field_Page.php83.33%100.00%100.00%
   Field_Phone.php94.55%100.00%100.00%
   Field_Poll.php93.75%100.00%100.00%
   Field_Post_Category.php85.00%100.00%100.00%
   Field_Post_Content.php82.35%100.00%100.00%
   Field_Post_Custom_Field.php50.00%100.00%100.00%
   Field_Post_Excerpt.php81.82%100.00%100.00%
   Field_Post_Image.php94.00%100.00%100.00%
   Field_Post_Tags.php90.91%100.00%100.00%
   Field_Post_Title.php81.82%100.00%100.00%
   Field_Product.php92.31%100.00%100.00%
   Field_Products.php84.32%100.00%100.00%
   Field_Quantity.php84.62%100.00%100.00%
   Field_Quiz.php89.74%100.00%100.00%
   Field_Radio.php94.74%100.00%100.00%
   Field_Rank.php95.24%100.00%100.00%
   Field_Rating.php95.24%100.00%100.00%
   Field_Repeater.php97.39%100.00%100.00%
   Field_Section.php90.74%100.00%100.00%
   Field_Select.php94.12%100.00%100.00%
   Field_Shipping.php92.86%100.00%100.00%
   Field_Signature.php71.11%100.00%100.00%
   Field_Slim.php82.35%100.00%100.00%
   Field_Slim_Post.php91.30%100.00%100.00%
   Field_Subtotal.php65.38%75.00%100.00%
   Field_Survey.php95.00%100.00%100.00%
   Field_Tax.php32.00%50.00%100.00%
   Field_Text.php81.82%100.00%100.00%
   Field_Textarea.php90.63%100.00%100.00%
   Field_Time.php81.82%100.00%100.00%
   Field_Tos.php92.59%100.00%100.00%
   Field_Total.php88.00%100.00%100.00%
   Field_Website.php85.71%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Fonts
   FlushCache.php92.31%100.00%100.00%
   LocalFile.php88.89%100.00%100.00%
   LocalFilesystem.php66.67%100.00%100.00%
   SupportsOtl.php87.50%100.00%100.00%
   TtfFontValidation.php84.62%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper
   Helper_Abstract_Addon.php94.09%100.00%100.00%
   Helper_Abstract_Config_Settings.php75.00%100.00%100.00%
   Helper_Abstract_Controller.php-100.00%100.00%
   Helper_Abstract_Field_Products.php89.47%66.67%100.00%
   Helper_Abstract_Fields.php95.62%95.65%100.00%
   Helper_Abstract_Fields_Input_Type.php84.00%100.00%100.00%
   Helper_Abstract_Form.php--100.00%
   Helper_Abstract_Model.php100.00%100.00%100.00%
   Helper_Abstract_Options.php77.13%79.66%100.00%
   Helper_Abstract_Pdf_Shortcode.php94.08%95.65%100.00%
   Helper_Abstract_View.php96.77%100.00%100.00%
   Helper_Async_Notifications.php64.29%100.00%100.00%
   Helper_Data.php98.29%100.00%100.00%
   Helper_Field_Container.php92.31%100.00%100.00%
   Helper_Field_Container_Gf25.php82.98%100.00%100.00%
   Helper_Field_Container_Void.php16.67%-100.00%
   Helper_Form.php80.65%64.29%100.00%
   Helper_Interface_Actions.php-100.00%100.00%
   Helper_Interface_Config.php50.00%100.00%100.00%
   Helper_Interface_Config_Settings.php50.00%100.00%100.00%
   Helper_Interface_Deprecated_Features.php-100.00%100.00%
   Helper_Interface_Extension_Settings.php-100.00%100.00%
   Helper_Interface_Extension_Uninstaller.php-100.00%100.00%
   Helper_Interface_Field_Pdf_Config.php50.00%100.00%100.00%
   Helper_Interface_Filters.php-100.00%100.00%
   Helper_Interface_Setup_TearDown.php50.00%100.00%100.00%
   Helper_Interface_Url_Signer.php-100.00%100.00%
   Helper_Logger.php-100.00%100.00%
   Helper_Misc.php88.95%100.00%100.00%
   Helper_Mpdf.php50.00%100.00%100.00%
   Helper_Notices.php92.98%100.00%100.00%
   Helper_Options_Fields.php98.13%100.00%100.00%
   Helper_PDF.php88.89%97.50%100.00%
   Helper_PDF_List_Table.php84.50%92.31%100.00%
   Helper_Pdf_Queue.php84.31%100.00%100.00%
   Helper_QueryPath.php80.00%100.00%100.00%
   Helper_Render_Health.php85.71%100.00%100.00%
   Helper_Sha256_Url_Signer.php90.91%100.00%100.00%
   Helper_Singleton.php100.00%100.00%100.00%
   Helper_Templates.php97.27%100.00%100.00%
   Helper_Trait_Logger.php25.00%-100.00%
   Helper_Trait_Removed_Methods.php100.00%100.00%100.00%
   Helper_Url_Signer.php85.09%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Licensing
   EDD_SL_Plugin_Updater.php94.83%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Log
   Context_Logger.php80.00%100.00%100.00%
   Logger.php86.89%100.00%100.00%
Table truncated to fit comment

🤖 PHPUnit coverage report

gravitypdf-bot and others added 2 commits October 26, 2026 17:36
… test

Image Hopper's fields (image_hopper / image_hopper_post) extend GF_Field_FileUpload under their own type, and
their get_input_type() only reports "fileupload" during submission and a few other requests, not on the PDF
settings screen. The field list now also takes every registered field type whose class extends
GF_Field_FileUpload, read from GF_Fields::get_all(), so GFAPI::get_fields_by_type() still walks nested fields.

GF_Field_FileUpload::to_array() only exists since Gravity Forms 2.10, while the plugin still supports 2.5+, and
the source-value filter runs on every rule check. The stored value is now decoded inline instead: empty and
"[]" both give an empty list.

The Playwright spec picks each File Upload field in the PDF conditional logic UI, checks the is / is not
operators and "Empty" value, saves, reloads to confirm the rule persisted, and checks only the entry with a file
gets a PDF link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Another gform_rule_source_value callback can run first and return an array, and json_decode()
throws a TypeError on one under PHP 8. The filter runs on every Gravity Forms conditional logic check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jakejackson1
jakejackson1 merged commit 91a295b into development Oct 26, 2026
19 checks passed
@jakejackson1
jakejackson1 deleted the fix-1619-file-upload-conditional-logic branch October 26, 2026 06:53
gravitypdf-bot added a commit that referenced this pull request Oct 26, 2026
Backport of #1961: source and PHPUnit only. The Playwright spec and PHPStan baseline change stay on development.

Co-authored-by: Claude Opus 5.5 <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.

File Upload Conditional Logic

2 participants