Submitted by: HHH Consulting Component: Pro Forms (nestable) — File field, Direct Upload to Server
Severity: Low — the form still saves. Leave Direct Upload off (the default) and the file is stored on the record. The loss is the progress state that Direct Upload would have shown.
Type: File handoff / field name
Environment
| Bricksforge | 4.0.0 |
| Bricks | 2.4 |
| WordPress | 7.1.1 |
| PHP | 8.2.33 |
| ACF PRO | 6.8.10 |
| Browser | Chromium |
| Theme | Bricks Child Theme 1.1 |
Observed on a photo add form that is not a Repeater. The same image was submitted twice: once with Direct Upload on, once with it off.
Summary
With Direct Upload to Server on, FilePond says the file has uploaded. The form then saves the post, but the ACF image field (and the featured image, when that is mapped to the same file field) stays empty.
With Direct Upload off, the same image is stored.
The file input is named form-field-{id}[]. A normal submit lets PHP turn that into form-field-{id}, and the save matches {id}. Direct Upload keeps the brackets, so the save looks for {id}[] and skips the file.
Steps to reproduce
- Add a Pro Form with a File field. Custom ID e.g.
photo-front. Turn Direct Upload to Server on. Do not put the field inside a Repeater. - Map that field to an ACF image field on Create Post (for example
uploaded_image={{photo-front}}). - On the front end, choose an image. Wait until FilePond reports the upload complete. Submit the form.
- Open the new post. The ACF image is empty.
- Turn Direct Upload off. Submit the same image again.
Expected: both submissions store the image on the ACF field.
Actual: only the submission with Direct Upload off stores the image. FilePond still reported success on the first attempt.
Evidence
Input name
bricksforge/includes/elements/pro-forms/elements/File.php (4.0.0):
$this->set_attribute('field', 'name', 'form-field-' . $id . '[]');
For a field whose id is photo-front, the name attribute is form-field-photo-front[].
What Direct Upload stores
FilePond’s server.process receives that name as fieldName and keeps it:
window.bricksforgeData.temporaryFileUploads.push({
file: uploadedFile,
field: fieldName,
});
handle_temporary_file_uploads() in includes/elements/pro-forms/actions/init.php uses $file['field'] as the array key. Nothing removes the trailing [].
process_file_group() then copies that key onto the file record:
'field' => $input_name,
So the saved record is keyed form-field-photo-front[].
What the save compares
includes/elements/pro-forms/actions/base.php, when resolving {{photo-front}}:
// $field is form-field-my_field. Strip the "form-field-" part concretely
$field = substr($file['field'], strpos($file['field'], 'form-field-') + strlen('form-field-'));
if ($field === $id) {
$id is photo-front. After the strip, $field is photo-front[]. The strict compare fails, so handle_file() is never called for that field. The same strip is used in handle_file_field_ids().
get_allowed_file_types_from_settings() also strips only form-field-, so it looks up photo-front[], misses the field, and falls back to the default MIME list. The file is not rejected. It is accepted, then not attached.
Why Direct Upload off works
With the checkbox off, the browser posts the file in the form. PHP’s $_FILES key for an input named form-field-photo-front[] is form-field-photo-front (the brackets are not part of the key). The same compare then yields photo-front, which matches the field id, and the image is written.
Why FilePond still says it uploaded
form_file_upload() writes the file into the Bricksforge temp directory and returns that path. FilePond treats that response as success. No media attachment is created at that step. The attachment is supposed to be created on submit, from the temp file, and that step is the one that skips the field.
Cause
Direct Upload and a normal submit do not use the same field name.
- Normal submit:
form-field-photo-front - Direct Upload:
form-field-photo-front[]
The save only removes the form-field- prefix. It never removes [], so the temp file is not applied to the field.
Suggested fix
Strip a trailing [] when the temp upload is recorded, and again when the field id is read back, so both paths produce photo-front:
$file_field = preg_replace('/\[\]$/', '', $file['field']);
$field = substr($file['field'], strpos($file['field'], 'form-field-') + strlen('form-field-'));
$field = preg_replace('/\[\]$/', '', $field);
Apply the second strip everywhere the prefix is removed (get_form_field_by_id, handle_file_field_ids, and the other file loop in base.php), and in get_allowed_file_types_from_settings().
What this is not
- Not a bad image. The same file is stored when Direct Upload is off.
- Not the known Repeater limit. Direct Upload is already unsupported inside a Repeater. This form is not a Repeater.
- Not a permissions or ACF return-format problem. The post is created. Only the file mapped from that field is missing.
Happy to test a patch on a staging install.