Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* 🎉 Feature: Install several PDF template zips at once
* 🎉 Feature: Drop template zips anywhere in the Template Manager
* 🎉 Feature: Install template zips that have the templates inside a folder
* 🎉 Feature: Use File Upload fields in PDF conditional logic, including fields built on them like Image Hopper
* 🔒 Security: Only sign PDF URLs when the shortcode or merge tag comes from a trusted source
* 🔒 Security: Cap signed PDF URLs at 1 year and the Logged Out Timeout at 1 week
* 🔒 Security: Restrict PDF template uploads and deletes to users who can install plugins
Expand Down
39 changes: 30 additions & 9 deletions src/Controller/Controller_Form_Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,24 +282,45 @@ public function clear_cached_pdf_settings( $form, $form_id, $meta_name ) {
}

/**
* Process entry meta conditional logic rules
* Process entry meta and File Upload conditional logic rules
*
* @param int|string $source_value The value of the rule’s configured field ID, entry meta, or custom property.
* @param array $rule The GF current rule object https://docs.gravityforms.com/conditional-logic-object/#rule
* @param array $form
* @param array $logic The GF conditional logic object https://docs.gravityforms.com/conditional-logic-object/
* @param array|null $entry The entry currently being processed, if available.
* @param int|string|array $source_value The value of the rule’s configured field ID, entry meta, or custom property.
* @param array $rule The GF current rule object https://docs.gravityforms.com/conditional-logic-object/#rule
* @param array $form
* @param array $logic The GF conditional logic object https://docs.gravityforms.com/conditional-logic-object/
* @param array|null $entry The entry currently being processed, if available.
*
* @return int|string
* @return int|string|array
*
* @since 6.9.0
*/
public function conditional_logic_set_rule_source_value( $source_value, $rule, $form, $logic, $entry ) {

$keys = array_keys( $this->data->get_conditional_logic_options( $form ) );
$target = $rule['fieldId'] ?? null;

if ( ! $entry || ! in_array( $target, $keys, true ) ) {
if ( ! $entry ) {
return $source_value;
}

/* Multi-file uploads are stored as JSON (an empty list is "[]"), so compare against the individual file URLs */
if ( \GFAPI::get_field( $form, $target ) instanceof \GF_Field_FileUpload ) {
if ( empty( $source_value ) ) {
return [];
}

/* Another gform_rule_source_value callback may have already converted it */
if ( is_array( $source_value ) ) {
return $source_value;
}

$files = json_decode( $source_value, true );

return is_array( $files ) ? $files : [ $source_value ];
}

$keys = array_keys( $this->data->get_conditional_logic_options( $form ) );

if ( ! in_array( $target, $keys, true ) ) {
return $source_value;
}

Expand Down
42 changes: 34 additions & 8 deletions src/Helper/Helper_Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,10 @@ public function get_conditional_logic_options( $form ): array {
}
}

/* Gravity Wiz Unique ID perk */
/* Fields GF doesn't offer in conditional logic, but which have a value once the entry is saved */
$post_submission_conditional_logic_field_types = [
'uid' => [
/* Gravity Wiz Unique ID perk */
'uid' => [
'operators' => [
'is' => 'is',
'isnot' => 'isNot',
Expand All @@ -547,16 +548,41 @@ public function get_conditional_logic_options( $form ): array {
'ends_with' => 'endsWith',
],
],

'fileupload' => [
'operators' => [
'is' => 'is',
'isnot' => 'isNot',
],
'choices' => [
[
'text' => esc_html__( 'Empty', 'gravity-pdf' ),
'value' => '',
],
],
],
];

$fields = \GFAPI::get_fields_by_type( $form, array_keys( $post_submission_conditional_logic_field_types ) );
/* Include field types built on the File Upload field, e.g. Image Hopper */
$file_upload_types = array_keys(
array_filter(
\GF_Fields::get_all(),
function ( $field ) {
return $field instanceof \GF_Field_FileUpload;
}
)
);

$fields = \GFAPI::get_fields_by_type( $form, array_merge( array_keys( $post_submission_conditional_logic_field_types ), $file_upload_types ) );

foreach ( $fields as $field ) {
$options[ $field->id ] = [
'label' => $field->label,
'value' => $field->id,
'operators' => rgars( $post_submission_conditional_logic_field_types, $field->type . '/operators', [] ),
];
$options[ $field->id ] = array_merge(
[
'label' => $field->label,
'value' => $field->id,
],
$post_submission_conditional_logic_field_types[ $field->type ] ?? $post_submission_conditional_logic_field_types['fileupload']
);
}

return $options;
Expand Down
110 changes: 110 additions & 0 deletions tests/phpunit/unit-tests/test-form-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -844,4 +844,114 @@ public function test_conditional_logic_is_value_match() {

}

public function test_conditional_logic_options_include_file_upload_fields() {
global $gfpdf;

$form = \GFAPI::get_form( $this->file_upload_form_id() );

$options = $gfpdf->data->get_conditional_logic_options( $form );

foreach ( [ 1, 2 ] as $field_id ) {
$this->assertSame( $field_id, $options[ $field_id ]['value'] );
$this->assertSame( [ 'is' => 'is', 'isnot' => 'isNot' ], $options[ $field_id ]['operators'] );
$this->assertSame( [ '' ], array_column( $options[ $field_id ]['choices'], 'value' ) );
}

$this->assertSame( 'Single', $options[1]['label'] );
}

public function test_conditional_logic_options_include_fields_extending_file_upload() {
global $gfpdf;

if ( ! \GF_Fields::exists( 'gfpdf_test_upload' ) ) {
\GF_Fields::register( new Test_File_Upload_Subclass() );
}

$form_id = (int) \GFAPI::add_form(
[
'title' => 'File Upload Subclass Conditional Logic',
'fields' => [ [ 'id' => 1, 'type' => 'gfpdf_test_upload', 'label' => 'Image Hopper' ] ],
]
);

$options = $gfpdf->data->get_conditional_logic_options( \GFAPI::get_form( $form_id ) );

$this->assertSame( 'Image Hopper', $options[1]['label'] );
$this->assertSame( [ 'is' => 'is', 'isnot' => 'isNot' ], $options[1]['operators'] );
}

/**
* @dataProvider provider_file_upload_conditional_logic
*/
public function test_file_upload_conditional_logic( $field_id, $value, $has_file ) {
global $gfpdf;

$entry = [
'id' => 1,
'form_id' => $this->file_upload_form_id(),
'1' => '',
'2' => '',
];

$entry[ $field_id ] = $value;

foreach ( [ 'is' => ! $has_file, 'isnot' => $has_file ] as $operator => $expected ) {
$settings = [
'conditionalLogic' => [
'actionType' => 'show',
'logicType' => 'all',
'rules' => [ [ 'fieldId' => (string) $field_id, 'operator' => $operator, 'value' => '' ] ],
],
];

$this->assertSame( $expected, $gfpdf->misc->conditional_logic_passes( $settings, $entry ), $operator );
}
}

public function test_conditional_logic_set_rule_source_value_lists_file_upload_urls() {
$form = \GFAPI::get_form( $this->file_upload_form_id() );
$entry = [ 'id' => 1 ];
$url = 'https://example.com/file.pdf';

foreach (
[
[ '1', '', [] ],
[ '1', $url, [ $url ] ],
[ '2', '[]', [] ],
[ '2', wp_json_encode( [ $url ] ), [ $url ] ],
[ '2', [ $url ], [ $url ] ],
] as [ $field_id, $value, $expected ]
) {
$rule = [ 'fieldId' => $field_id ];
$this->assertSame( $expected, $this->controller->conditional_logic_set_rule_source_value( $value, $rule, $form, [], $entry ) );
}
}

public function provider_file_upload_conditional_logic() {
$url = 'https://example.com/wp-content/uploads/gravity_forms/1/file.pdf';

return [
'single, no file' => [ 1, '', false ],
'single, file' => [ 1, $url, true ],
'multi, no file' => [ 2, '', false ],
'multi, empty JSON list' => [ 2, '[]', false ],
'multi, files' => [ 2, wp_json_encode( [ $url, $url ] ), true ],
];
}

private function file_upload_form_id(): int {
return (int) \GFAPI::add_form(
[
'title' => 'File Upload Conditional Logic',
'fields' => [
new \GF_Field_FileUpload( [ 'id' => 1, 'label' => 'Single' ] ),
new \GF_Field_FileUpload( [ 'id' => 2, 'label' => 'Multi', 'multipleFiles' => true ] ),
],
]
);
}
}

class Test_File_Upload_Subclass extends \GF_Field_FileUpload {
public $type = 'gfpdf_test_upload';
}
Loading