WordPress / ACF PRO Files Field Custom Upload Directory

Sometimes you want uploads to end up somewhere other than the default WordPress uploads directory. For that WordPress provides the upload_dir filter, but how does that work with ACF PRO file fields?

For a recent project certain uploaded files needed to be placed into a protected S2Member uploads directory, which existed as a completely separate location from the wp-content/uploads directory. But if you needed to move uploads within the uploads directory that is easy to do as well.

Modifying WordPress Upload Location

To modify the WordPress upload location you would add the below to your functions.php file:


add_filter( 'upload_dir', 'my_custom_upload_directory' );

function my_custom_upload_directory( $param ) {
        
  $folder = '/my-special-folder';

  $param['path'] = $param['path'] . $folder;
  $param['url'] = $param['url'] . $folder;
  
  return $param; 

}

Your new upload destination would be: http://mysite.com/wp-content/uploads/2016/08/my-special-folder

The /2016/08 is automatically added by WordPress if you have the “Organize my uploads into month- and year-based folders” checked under Settings > Media.

If you wanted to have your new directory located directly under /wp-content/uploads you would do the following:


add_filter( 'upload_dir', 'my_custom_upload_directory' );

function my_custom_upload_directory( $param ) {
        
  $folder = '/my-special-folder';

  $param['path'] = $param['basedir'] . $folder;
  $param['url'] = $param['baseurl'] . $folder;
  $param['subdir'] = '/';
  
  return $param; 

}

Then your new upload destination would be: http://mysite.com/wp-content/uploads/my-special-folder

Changing the Default Media Folder

If you want to change the default Media upload folder entirely you can add the following to your wp-config.php file before the line that reads require_once(ABSPATH . 'wp-settings.php');


define('UPLOADS', 'wp-content/my-special-folder');

Then your new upload destination would be: http://mysite.com/wp-content/my-special-folder for all uploaded media.

Modifying WordPress Upload Location Outside /wp-content/uploads

As mentioned above, using S2Member for secure downloads requires placing restricted files in a directory that is located at /wp-content/plugins/s2member-files. To accomplish that the paths just needed to be updated to point to the plugins directory:


add_filter( 'upload_dir', 'my_custom_upload_directory' );

function my_custom__upload_directory( $param ) {
        
  $folder = '/s2member-files';

  $param['path'] = WP_PLUGIN_DIR . $folder;
  $param['url'] = WP_PLUGIN_URL . $folder;
  $param['subdir'] = $folder;
  $param['basedir'] = WP_PLUGIN_DIR;
  $param['baseurl'] = WP_PLUGIN_URL;

  return $param;

}

Now the uploaded file are all going into the restricted directory. But wait! Only certain files should go into the restricted directory.

Since this site also used ACF PRO, a custom field was created for these special file uploads called secure_files. Fortunately ACF PRO has a filter run before uploads that allows us to do some modifications. The filter also allows you to target specific fields that it should trigger on, so the filter for the field named secure_files in functions.php would look like:


add_filter( 'acf/upload_prefilter/name=secure_files', 'secure_upload_prefilter' );

function secure_upload_prefilter( $errors ) {

  add_filter( 'upload_dir', 'secure_upload_directory' );

  return $errors;

}

function secure_upload_directory( $param ) {
        
  $folder = '/s2member-files';

  $param['path'] = WP_PLUGIN_DIR . $folder;
  $param['url'] = WP_PLUGIN_URL . $folder;
  $param['subdir'] = $folder;
  $param['basedir'] = WP_PLUGIN_DIR;
  $param['baseurl'] = WP_PLUGIN_URL;

  return $param;

}

Here the upload_dir filter is moved into a function that only gets run if the field named secure_files triggers it.

Now all files uploaded using the custom field get put in the restricted /wp-content/plugins/s2member-files directory. Nice!

But there is one remaining issue, if you look at the post where you uploaded the file – it doesn’t show that a file has been uploaded. This is because the field is still looking at the default WordPress upload location for the file, and since it doesn’t find it there it’s not listed.

ACF PRO provides another filter that can be used to modify a field before it’s displayed. This filter also allows you to target specific field names as well, in this case secure_files:


add_filter( 'acf/prepare_field/name=secure_files', 'secure_files_field_display' );

function secure_files_field_display( $field ) {

  // update paths accordingly before displaying link to file
  add_filter( 'upload_dir', 'secure_upload_directory' );

  return $field;

}

All that’s happening in this function is calling the upload_dir filter before rendering the field so it uses the custom directory, rather than the default WordPress uploads directory.

The completed code in functions.php would then look like:


add_filter( 'acf/upload_prefilter/name=secure_files', 'secure_upload_prefilter' );
add_filter( 'acf/prepare_field/name=secure_files', 'secure_files_field_display' );

function secure_upload_prefilter( $errors ) {

  add_filter( 'upload_dir', 'secure_upload_directory' );

  return $errors;

}

function secure_upload_directory( $param ) {
        
  $folder = '/s2member-files';

  $param['path'] = WP_PLUGIN_DIR . $folder;
  $param['url'] = WP_PLUGIN_URL . $folder;
  $param['subdir'] = $folder;
  $param['basedir'] = WP_PLUGIN_DIR;
  $param['baseurl'] = WP_PLUGIN_URL;

  return $param;

}

function secure_files_field_display( $field ) {

  // update paths accordingly before displaying link to file
  add_filter( 'upload_dir', 'secure_upload_directory' );

  return $field;

}