Rename Uploaded Files

Rename uploaded files for Gravity Forms. You can create a static naming template or using merge tags to base names on user input.

Features: + supports single and multi-file upload fields + flexible naming template with support for static and dynamic values via GF merge tags

Uses: + add a prefix or suffix to file uploads + include identifying submitted data in the file name like the user’s first and last name

Read the Walkthrough

Code

Filename: gw-gravity-forms-rename-uploaded-files.php

<?php
/**
 * Gravity Wiz // Gravity Forms // Rename Uploaded Files
 * https://gravitywiz.com/rename-uploaded-files-for-gravity-form/
 *
 * Rename uploaded files for Gravity Forms. You can create a static naming template or using merge tags to base names on user input.
 *
 * Features:
 *  + supports single and multi-file upload fields
 *  + flexible naming template with support for static and dynamic values via GF merge tags
 *
 * Uses:
 *  + add a prefix or suffix to file uploads
 *  + include identifying submitted data in the file name like the user's first and last name
 *
 * @version   2.6
 * @author    David Smith <david@gravitywiz.com>
 * @license   GPL-2.0+
 * @link      https://gravitywiz.com/rename-uploaded-files-for-gravity-form/
 */
class GW_Rename_Uploaded_Files {

	public $_args;

	public function __construct( $args = array() ) {

		// set our default arguments, parse against the provided arguments, and store for use throughout the class
		$this->_args = wp_parse_args( $args, array(
			'form_id'          => false,
			'field_id'         => false,
			'template'         => '',
			'ignore_extension' => false,
		) );

		// do version check in the init to make sure if GF is going to be loaded, it is already loaded
		add_action( 'init', array( $this, 'init' ) );

	}

	public function init() {

		// make sure we're running the required minimum version of Gravity Forms
		if ( ! is_callable( array( 'GFFormsModel', 'get_physical_file_path' ) ) ) {
			return;
		}

		add_filter( 'gform_entry_post_save', array( $this, 'rename_uploaded_files' ), 9, 2 );
		add_filter( 'gform_entry_post_save', array( $this, 'stash_uploaded_files' ), 99, 2 );

		add_action( 'gform_after_update_entry', array( $this, 'rename_uploaded_files_after_update' ), 9, 2 );
		add_action( 'gform_after_update_entry', array( $this, 'stash_uploaded_files_after_update' ), 99, 2 );

	}

	function rename_uploaded_files( $entry, $form ) {

		if ( ! $this->is_applicable_form( $form ) ) {
			return $entry;
		}

		foreach ( $form['fields'] as &$field ) {

			if ( ! $this->is_applicable_field( $field ) ) {
				continue;
			}

			$uploaded_files = rgar( $entry, $field->id );

			if ( empty( $uploaded_files ) ) {
				continue;
			}

			$uploaded_files = $this->parse_files( $uploaded_files, $field );
			$stashed_files  = $this->parse_files( gform_get_meta( $entry['id'], 'gprf_stashed_files' ), $field );
			$renamed_files  = array();

			foreach ( $uploaded_files as $_file ) {

				// Don't rename the same files twice.
				if ( in_array( $_file, $stashed_files ) ) {
					$renamed_files[] = $_file;
					continue;
				}

				$dir  = wp_upload_dir();
				$dir  = $this->get_upload_dir( $form['id'] );
				$file = str_replace( $dir['url'], $dir['path'], $_file );

				if ( ! file_exists( $file ) ) {
					continue;
				}

				$renamed_file = $this->rename_file( $file, $entry );

				if ( ! is_dir( dirname( $renamed_file ) ) ) {
					wp_mkdir_p( dirname( $renamed_file ) );
				}

				$result = rename( $file, $renamed_file );

				$renamed_files[] = $this->get_url_by_path( $renamed_file, $form['id'] );

			}

			// In cases where 3rd party add-ons offload the image to a remote location, no images can be renamed.
			if ( empty( $renamed_files ) ) {
				continue;
			}

			if ( $field->get_input_type() == 'post_image' ) {
				$value = str_replace( $uploaded_files[0], $renamed_files[0], rgar( $entry, $field->id ) );
			} elseif ( $field->multipleFiles ) {
				$value = json_encode( $renamed_files );
			} else {
				$value = $renamed_files[0];
			}

			GFAPI::update_entry_field( $entry['id'], $field->id, $value );

			$entry[ $field->id ] = $value;

		}

		return $entry;
	}

	function get_upload_dir( $form_id ) {
		$dir         = GFFormsModel::get_file_upload_path( $form_id, 'PLACEHOLDER' );
		$dir['path'] = dirname( $dir['path'] );
		$dir['url']  = dirname( $dir['url'] );
		return $dir;
	}

	function rename_uploaded_files_after_update( $form, $entry_id ) {
		$entry = GFAPI::get_entry( $entry_id );
		$this->rename_uploaded_files( $entry, $form );
	}

	/**
	 * Stash the "final" version of the files after other add-ons have had a chance to interact with them.
	 *
	 * @param $entry
	 * @param $form
	 */
	function stash_uploaded_files( $entry, $form ) {

		foreach ( $form['fields'] as &$field ) {

			if ( ! $this->is_applicable_field( $field ) ) {
				continue;
			}

			$uploaded_files         = rgar( $entry, $field->id );
			$existing_stashed_files = gform_get_meta( $entry['id'], 'gprf_stashed_files' );

			if ( $this->is_json( $uploaded_files ) ) {
				$uploaded_files = json_decode( $uploaded_files, ARRAY_A );
			}

			if ( $this->is_json( $existing_stashed_files ) ) {
				$existing_stashed_files = json_decode( $existing_stashed_files, ARRAY_A );
			}

			/* Convert single files to array of files. */
			if ( ! is_array( $existing_stashed_files ) ) {
				$existing_stashed_files = $existing_stashed_files ? array( $existing_stashed_files ) : array();
			}

			if ( ! is_array( $uploaded_files ) ) {
				$uploaded_files = $uploaded_files ? array( $uploaded_files ) : array();
			}

			if ( ! empty( $existing_stashed_files ) ) {
				$uploaded_files = array_merge( $existing_stashed_files, $uploaded_files );
			}

			gform_update_meta( $entry['id'], 'gprf_stashed_files', json_encode( $uploaded_files ) );

		}

		return $entry;
	}

	/**
	 * Check whether a string is JSON or not.
	 *
	 * @param $string string String to test.
	 *
	 * @return bool Whether the string is JSON.
	 */
	function is_json( $string ) {
		if ( method_exists( 'GFCommon', 'is_json' ) ) {
			return GFCommon::is_json( $string );
		}

		// Duplicate contents of GFCommon::is_json() here to supports versions of GF older than GF 2.5.
		// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
		if ( is_string( $string ) && in_array( substr( $string, 0, 1 ), array( '{', '[' ) ) && is_array( json_decode( $string, ARRAY_A ) ) ) {
			return true;
		}

		return false;
	}

	function stash_uploaded_files_after_update( $form, $entry_id ) {
		$entry = GFAPI::get_entry( $entry_id );
		$this->stash_uploaded_files( $entry, $form );
	}

	function rename_file( $file, $entry ) {

		$new_file = $this->get_renamed_filepath( $this->_args['template'], $file, $entry );
		$new_file = $this->increment_file( $new_file );

		return $new_file;
	}

	function increment_file( $file ) {

		$file_path = GFFormsModel::get_physical_file_path( $file );
		$pathinfo  = pathinfo( $file_path );
		$counter   = 1;

		if ( $this->_args['ignore_extension'] ) {
			while ( glob( str_replace( ".{$pathinfo['extension']}", '.*', $file_path ) ) ) {
				$file_path = str_replace( ".{$pathinfo['extension']}", "{$counter}.{$pathinfo['extension']}", GFFormsModel::get_physical_file_path( $file ) );
				$counter ++;
			}
		} else {
			// increment the filename if it already exists (i.e. balloons.jpg, balloons1.jpg, balloons2.jpg)
			while ( file_exists( $file_path ) ) {
				$file_path = str_replace( ".{$pathinfo['extension']}", "{$counter}.{$pathinfo['extension']}", GFFormsModel::get_physical_file_path( $file ) );
				$counter ++;
			}
		}

		$file = str_replace( basename( $file ), basename( $file_path ), $file );

		return $file;
	}

	function is_path( $filename ) {
		return strpos( $filename, '/' ) !== false;
	}

	function get_renamed_filepath( $template, $file, $entry ) {
		$info = pathinfo( $file );

		// replace our custom "{filename}" psuedo-merge-tag
		$filename = str_replace( '{filename}', $info['filename'], $template );

		// replace merge tags
		$form     = GFAPI::get_form( $entry['form_id'] );
		$filename = GFCommon::replace_variables( $filename, $form, $entry, false, true, false, 'text' );
		// make sure filename is "clean". This includes removing any user inputted items such as "../", "/usr/bin" etc
		$filename = $this->clean( $filename );

		if ( strpos( $template, '/' ) === 0 ) {
			$dir      = wp_upload_dir();
			$filepath = $dir['basedir'];
		} else {
			$filepath = $info['dirname'] . '/';
		}

		$filepath .= $filename . '.' . $info['extension'];

		return $filepath;
	}

	function is_applicable_form( $form ) {

		$form_id = isset( $form['id'] ) ? $form['id'] : $form;

		return $form_id == $this->_args['form_id'];
	}

	function is_applicable_field( $field ) {

		$is_file_upload_field   = in_array( GFFormsModel::get_input_type( $field ), array( 'fileupload', 'post_image' ) );
		$is_applicable_field_id = $this->_args['field_id'] ? $field['id'] == $this->_args['field_id'] : true;

		return $is_file_upload_field && $is_applicable_field_id;
	}

	function clean( $str ) {
		return sanitize_file_name( $str );
	}

	function get_url_by_path( $file, $form_id ) {

		$dir = $this->get_upload_dir( $form_id );
		$url = str_replace( $dir['path'], $dir['url'], $file );

		return $url;
	}

	function parse_files( $files, $field ) {

		if ( empty( $files ) ) {
			return array();
		}

		if ( $this->is_json( $files ) ) {
			$files = json_decode( $files );
		} elseif ( $field->get_input_type() === 'post_image' ) {
			$file_bits = explode( '|:|', $files );
			$files     = array( $file_bits[0] );
		} else {
			$files = array( $files );
		}

		return $files;
	}

}

# Configuration

new GW_Rename_Uploaded_Files( array(
	'form_id'          => 628,
	'field_id'         => 3,
	// most merge tags are supported, original file extension is preserved
	'template'         => '{Name (First):1.3}-{Name (Last):1.6}-{filename}',
	// Ignore extension when renaming files and keep them in sequence (e.g. a.jpg, a1.png, a2.pdf etc.)
	'ignore_extension' => false,
) );

Leave a Reply

Your email address will not be published. Required fields are marked *

  • Trouble installing this snippet? See our troubleshooting tips.
  • Need to include code? Create a gist and link to it in your comment.
  • Reporting a bug? Provide a URL where this issue can be recreated.

By commenting, I understand that I may receive emails related to Gravity Wiz and can unsubscribe at any time.