Currently when a file is uploaded the webform module will automatically add it in a folder named after the submission ID, under private:webform/WEBFORM_KEY.

So, you end up with a structure like:

$ tree
.
├── 301
│   └── file_name.jpg
├── 302
│   └── file_name.jpg
├── 303
│   └── file_name.jpg
├── 304
│   ├── file_name1.jpg
│   ├── file_name2.jpg
│   └── file_name3.jpg
└── _sid_

I suggest to give the option to admins to decide whether they want this structure or an option to save all files under one folder, without the subfolders.
With #2941286: Rename file uploads for v8.x commited we have control over the filename anyway, so using subfolders for every submission might not be needed for all projects.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bserem created an issue. See original summary.

jrockowitz’s picture

Status: Active » Closed (works as designed)

This change would break the exporting of uploaded files into a zipped archive which is a key feature. By using 'webform/WEBFORM_KEY' as the upload path we are able to copy and very quickly zip all the files uploaded for a single submission.

The workaround/solution is you can set the #upload_location as a custom element property and chose the upload folder for your files.

bserem’s picture

I didn't understood the part with the custom element, but I guess if someone wants the files stored in a specific location then this would be fine for his downloaded archive too.

jrockowitz’s picture

The zip archiving only looks for files stored in webform/WEBFORM_KEY/SID.

The #upload_location would be set as a custom property.

b.khouy’s picture

@jrockowitz thank you for #upload_location property suggestion, does the Yaml field Custom properties support tokens?

Also for your #2 comment, I think we can customize file upload location without break zipping uploaded files. Lets say we want to add a textfield field with tokens support to the webform settings tab, that textfield will store the directory within the files should be stored my-directory for example, and implicitly prefix that directory by webform/WEBFORM_KEY and suffix it with /SID then the final upload folder will be webform/WEBFORM_KEY/my-directory/SID, does this logic either break downloading uploaded files process, what do you think?

jrockowitz’s picture

Using webform/WEBFORM_KEY/my-directory/SID will break the downloading of exported files.

MikaT’s picture

Note that the custom properties doesn't seem to support tokens so one might need to extend the WebformManagedFileBase and in that new class override the getUploadLocation.

As I needed token support, I did it so that I created new WeformElement extending the WebformManagedFileBase that introduced new field where the upload location can be entered. Then in getUploadLocation get the new field value from $element, run $this->replaceTokens($element) and then call the parent getUploadLocation.


namespace Drupal\my_module\Plugin\WebformElement;

use Drupal\Core\Form\FormStateInterface;
use Drupal\webform\Plugin\WebformElement\WebformManagedFileBase;
use Drupal\webform\WebformInterface;

/**
 * Custom version of the webform document file.
 *
 * @WebformElement(
 *   id = "custom_webform_managed_file",
 *   label = @Translation("Custom managed file"),
 *   description = @Translation("Custom version of the webform managed file."),
 *   category = @Translation("File upload elements"),
 *   states_wrapper = TRUE,
 *   dependencies = {
 *     "file",
 *   }
 * )
 */
class CustomManagedFile extends WebformManagedFileBase {

  /**
   * {@inheritdoc}
   */
  protected function defineDefaultProperties() {
    return parent::defineDefaultProperties() + [
      'file_directory' => '',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);

    $form['file']['file_directory'] = [
      '#type' => 'textfield',
      '#title' => $this->t('File directory'),
      '#description' => $this->t('Optional subdirectory within the upload destination where files will be stored. Do not include preceding or trailing slashes.'),
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  protected function getUploadLocation(array $element, WebformInterface $webform) {
    if (empty($element['#file_directory'])) {
      return parent::getUploadLocation($element, $webform);
    }
    $uriScheme = $this->getUriScheme($element);
    // Replace tokens.
    $this->replaceTokens($element);
    // Alter the element so we can call parent.
    $element['#upload_location'] = $uriScheme . '://' . $element['#file_directory'];

    return parent::getUploadLocation($element, $webform);
  }

  /**
   * {@inheritdoc}
   */
  public function getItemFormat(array $element): string {
    return 'link';
  }

}

Remember to create an element matching the ID of the webform element

<?php

namespace Drupal\ssp_site\Element;

use Drupal\webform\Element\WebformManagedFileBase;

/**
 * Provides a webform element for an 'custom_webform_managed_file' element.
 *
 * @FormElement("custom_webform_managed_file")
 */
class CustomManagedFile extends WebformManagedFileBase {

}