GFFormsModel::get_upload_root(), 'url' => GFFormsModel::get_upload_url_root(), ); } return $path_info; } /** * Gets the default upload roots using the form ID and current time. * * @since 2.5.16 * * @param int $form_id The form ID to create the root for, * * @return string[] The root path and url. */ public static function get_default_upload_roots( $form_id ) { $cached_default_root = rgar( self::$forms_default_upload_roots, $form_id ); if ( $cached_default_root ) { return $cached_default_root; } // Generate the yearly and monthly dirs $time = current_time( 'mysql' ); $y = substr( $time, 0, 4 ); $m = substr( $time, 5, 2 ); $default_target_root = GFFormsModel::get_upload_path( $form_id ) . "/$y/$m/"; $default_target_root_url = GFFormsModel::get_upload_url( $form_id ) . "/$y/$m/"; self::$forms_default_upload_roots[ $form_id ] = array( 'path' => $default_target_root, 'url' => $default_target_root_url, 'y' => $y, 'm' => $m, ); return self::$forms_default_upload_roots[ $form_id ]; } /** * Returns the default file upload root and url for files stored by the provided form. * * @since 2.5.16 * * @param integer $form_id The form ID of the form that will be used to generate the directory name. * * @return array */ public static function get_upload_root_info( $form_id ) { $cached_root = rgar( self::$forms_upload_roots, $form_id ); if ( $cached_root ) { return $cached_root; } $default_upload_root_info = self::get_default_upload_roots( $form_id ); self::$forms_upload_roots[ $form_id ] = gf_apply_filters( array( 'gform_upload_path', $form_id ), $default_upload_root_info, $form_id ); return self::$forms_upload_roots[ $form_id ]; } public function get_form_editor_field_title() { return esc_attr__( 'File Upload', 'gravityforms' ); } /** * Returns the field's form editor description. * * @since 2.5 * * @return string */ public function get_form_editor_field_description() { return esc_attr__( 'Allows users to upload a file.', 'gravityforms' ); } /** * Returns the field's form editor icon. * * This could be an icon url or a gform-icon class. * * @since 2.5 * * @return string */ public function get_form_editor_field_icon() { return 'gform-icon--upload'; } function get_form_editor_field_settings() { return array( 'conditional_logic_field_setting', 'error_message_setting', 'label_setting', 'label_placement_setting', 'admin_label_setting', 'rules_setting', 'file_extensions_setting', 'file_size_setting', 'multiple_files_setting', 'visibility_setting', 'description_setting', 'css_class_setting', ); } public function validate( $value, $form ) { $file_names = array(); $input_name = 'input_' . $this->id; GFCommon::log_debug( __METHOD__ . '(): Validating field ' . $input_name ); $allowed_extensions = ! empty( $this->allowedExtensions ) ? GFCommon::clean_extensions( explode( ',', strtolower( $this->allowedExtensions ) ) ) : array(); if ( $this->multipleFiles ) { $file_names = isset( GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] : array(); } elseif ( ! empty( $_FILES[ $input_name ] ) ) { $max_upload_size_in_bytes = isset( $this->maxFileSize ) && $this->maxFileSize > 0 ? $this->maxFileSize * 1048576 : wp_max_upload_size(); $max_upload_size_in_mb = $max_upload_size_in_bytes / 1048576; if ( ! empty( $_FILES[ $input_name ]['name'] ) && $_FILES[ $input_name ]['error'] > 0 ) { $uploaded_file_name = isset( GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] : ''; if ( empty( $uploaded_file_name ) ) { $this->failed_validation = true; switch ( $_FILES[ $input_name ]['error'] ) { case UPLOAD_ERR_INI_SIZE : case UPLOAD_ERR_FORM_SIZE : GFCommon::log_debug( __METHOD__ . '(): File ' . $_FILES[ $input_name ]['name'] . ' exceeds size limit. Maximum file size: ' . $max_upload_size_in_mb . 'MB' ); $fileupload_validation_message = sprintf( esc_html__( 'File exceeds size limit. Maximum file size: %dMB', 'gravityforms' ), $max_upload_size_in_mb ); break; default : GFCommon::log_debug( __METHOD__ . '(): The following error occurred while uploading - ' . $_FILES[ $input_name ]['error'] ); $fileupload_validation_message = sprintf( esc_html__( 'There was an error while uploading the file. Error code: %d', 'gravityforms' ), $_FILES[ $input_name ]['error'] ); } $this->validation_message = empty( $this->errorMessage ) ? $fileupload_validation_message : $this->errorMessage; return; } } elseif ( $_FILES[ $input_name ]['size'] > 0 && $_FILES[ $input_name ]['size'] > $max_upload_size_in_bytes ) { $this->failed_validation = true; GFCommon::log_debug( __METHOD__ . '(): File ' . $_FILES[ $input_name ]['name'] . ' exceeds size limit. Maximum file size: ' . $max_upload_size_in_mb . 'MB' ); $this->validation_message = sprintf( esc_html__( 'File exceeds size limit. Maximum file size: %dMB', 'gravityforms' ), $max_upload_size_in_mb ); return; } /** * A filter to allow or disallow whitelisting when uploading a file * * @param bool false To set upload whitelisting to true or false (default is false, which means it is enabled) */ $whitelisting_disabled = apply_filters( 'gform_file_upload_whitelisting_disabled', false ); if ( ! empty( $_FILES[ $input_name ]['name'] ) && ! $whitelisting_disabled ) { $check_result = GFCommon::check_type_and_ext( $_FILES[ $input_name ] ); if ( is_wp_error( $check_result ) ) { $this->failed_validation = true; GFCommon::log_debug( sprintf( '%s(): %s; %s', __METHOD__, $check_result->get_error_code(), $check_result->get_error_message() ) ); $this->validation_message = esc_html__( 'The uploaded file type is not allowed.', 'gravityforms' ); return; } } $single_file_name = $_FILES[ $input_name ]['name']; $file_names = array( array( 'uploaded_filename' => $single_file_name ) ); } foreach ( $file_names as $file_name ) { GFCommon::log_debug( __METHOD__ . '(): Validating file upload for ' . $file_name['uploaded_filename'] ); $info = pathinfo( rgar( $file_name, 'uploaded_filename' ) ); if ( empty( $allowed_extensions ) ) { if ( GFCommon::file_name_has_disallowed_extension( rgar( $file_name, 'uploaded_filename' ) ) ) { GFCommon::log_debug( __METHOD__ . '(): The file has a disallowed extension, failing validation.' ); $this->failed_validation = true; $this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'The uploaded file type is not allowed.', 'gravityforms' ) : $this->errorMessage; } } else { if ( ! empty( $info['basename'] ) && ! GFCommon::match_file_extension( rgar( $file_name, 'uploaded_filename' ), $allowed_extensions ) ) { GFCommon::log_debug( __METHOD__ . '(): The file is of a type that cannot be uploaded, failing validation.' ); $this->failed_validation = true; $this->validation_message = empty( $this->errorMessage ) ? sprintf( esc_html__( 'The uploaded file type is not allowed. Must be one of the following: %s', 'gravityforms' ), strtolower( implode( ', ', GFCommon::clean_extensions( explode( ',', $this->allowedExtensions ) ) ) ) ) : $this->errorMessage; } } } GFCommon::log_debug( __METHOD__ . '(): Validation complete.' ); } public function get_first_input_id( $form ) { return $this->multipleFiles ? 'gform_browse_button_' . $form['id'] . '_' . $this->id : 'input_' . $form['id'] . '_' . $this->id; } public function get_field_input( $form, $value = '', $entry = null ) { $lead_id = absint( rgar( $entry, 'id' ) ); $form_id = absint( $form['id'] ); $is_entry_detail = $this->is_entry_detail(); $is_form_editor = $this->is_form_editor(); $id = absint( $this->id ); $field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id"; $size = $this->size; $class_suffix = $is_entry_detail ? '_admin' : ''; $class = $size . $class_suffix; $class = esc_attr( $class ); $disabled_text = $is_form_editor ? 'disabled="disabled"' : ''; $tabindex = $this->get_tabindex(); $multiple_files = $this->multipleFiles; $file_list_id = 'gform_preview_' . $form_id . '_' . $id; // Generate upload rules messages ( allowed extensions, max no. of files, max file size ). $upload_rules_messages = array(); // Extensions. $allowed_extensions = ! empty( $this->allowedExtensions ) ? join( ',', GFCommon::clean_extensions( explode( ',', strtolower( $this->allowedExtensions ) ) ) ) : array(); if ( ! empty( $allowed_extensions ) ) { $upload_rules_messages[] = esc_attr( sprintf( __( 'Accepted file types: %s', 'gravityforms' ), str_replace( ',', ', ', $allowed_extensions ) ) ); } // File size. $max_upload_size = $this->maxFileSize > 0 ? $this->maxFileSize * 1048576 : wp_max_upload_size(); // translators: %s is replaced with a numeric string representing the maximum file size $upload_rules_messages[] = esc_attr( sprintf( __( 'Max. file size: %s', 'gravityforms' ), GFCommon::format_file_size( $max_upload_size ) ) ); // No. of files. $max_files = ( $multiple_files && $this->maxFiles > 0 ) ? $this->maxFiles : 0; if ( $max_files ) { // translators: %s is replaced with a numeric string representing the maximum number of files $upload_rules_messages[] = esc_attr( sprintf( __( 'Max. files: %s', 'gravityforms' ), $max_files ) ); } $rules_messages = implode( ', ', $upload_rules_messages ) . '.'; $rules_messages_id = empty( $rules_messages ) ? '' : "gfield_upload_rules_{$this->formId}_{$this->id}"; $describedby = $this->get_aria_describedby( array( $rules_messages_id ) ); if ( $multiple_files ) { $upload_action_url = trailingslashit( site_url() ) . '?gf_page=' . GFCommon::get_upload_page_slug(); $browse_button_id = 'gform_browse_button_' . $form_id . '_' . $id; $container_id = 'gform_multifile_upload_' . $form_id . '_' . $id; $drag_drop_id = 'gform_drag_drop_area_' . $form_id . '_' . $id; $validation_message_id = 'gform_multifile_messages_' . $form_id . '_' . $id; $messages_id = "gform_multifile_messages_{$form_id}_{$id}"; if ( empty( $allowed_extensions ) ) { $allowed_extensions = '*'; } $disallowed_extensions = GFCommon::get_disallowed_file_extensions(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && 'rg_change_input_type' === rgpost( 'action' ) ) { $plupload_init = array(); } else { $plupload_init = array( 'runtimes' => 'html5,flash,html4', 'browse_button' => $browse_button_id, 'container' => $container_id, 'drop_element' => $drag_drop_id, 'filelist' => $file_list_id, 'unique_names' => true, 'file_data_name' => 'file', /*'chunk_size' => '10mb',*/ // chunking doesn't currently have very good cross-browser support 'url' => $upload_action_url, 'flash_swf_url' => includes_url( 'js/plupload/plupload.flash.swf' ), 'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ), 'filters' => array( 'mime_types' => array( array( 'title' => __( 'Allowed Files', 'gravityforms' ), 'extensions' => $allowed_extensions ) ), 'max_file_size' => $max_upload_size . 'b', ), 'multipart' => true, 'urlstream_upload' => false, 'multipart_params' => array( 'form_id' => $form_id, 'field_id' => $id, ), 'gf_vars' => array( 'max_files' => $max_files, 'message_id' => $messages_id, 'disallowed_extensions' => $disallowed_extensions, ) ); if ( GFCommon::form_requires_login( $form ) ) { $plupload_init['multipart_params'][ '_gform_file_upload_nonce_' . $form_id ] = wp_create_nonce( 'gform_file_upload_' . $form_id, '_gform_file_upload_nonce_' . $form_id ); } // plupload 2 was introduced in WordPress 3.9. Plupload 1 accepts a slightly different init array. if ( version_compare( get_bloginfo( 'version' ), '3.9-RC1', '<' ) ) { $plupload_init['max_file_size'] = $max_upload_size . 'b'; $plupload_init['filters'] = array( array( 'title' => __( 'Allowed Files', 'gravityforms' ), 'extensions' => $allowed_extensions ) ); } } $plupload_init = gf_apply_filters( array( 'gform_plupload_settings', $form_id ), $plupload_init, $form_id, $this ); $drop_files_here_text = esc_html__( 'Drop files here or', 'gravityforms' ); $select_files_text = esc_attr__( 'Select files', 'gravityforms' ); $plupload_init_json = htmlspecialchars( json_encode( $plupload_init ), ENT_QUOTES, 'UTF-8' ); $upload = "
{$drop_files_here_text}
"; $upload .= $rules_messages ? "{$rules_messages}" : ''; $upload .= " "; if ( $is_entry_detail ) { $upload .= sprintf( '', $id, esc_attr( $value ) ); } } else { $upload = ''; if ( $max_upload_size <= 2047 * 1048576 ) { // MAX_FILE_SIZE > 2048MB fails. The file size is checked anyway once uploaded, so it's not necessary. $upload = sprintf( "", $max_upload_size ); } $live_validation_message_id = 'live_validation_message_' . $form_id . '_' . $id; $upload .= sprintf( "", $id, $field_id, esc_attr( $class ), $describedby, esc_attr( $max_upload_size ), $disabled_text ); $upload .= $rules_messages ? "{$rules_messages}" : ''; $upload .= "
"; } if ( $is_entry_detail && ! empty( $value ) ) { // edit entry $file_urls = $multiple_files ? json_decode( $value ) : array( $value ); $upload_display = $multiple_files ? '' : "style='display:none'"; $preview = "
$upload
"; $preview .= sprintf( "
", $file_list_id ); $preview .= sprintf( "
", $id ); foreach ( $file_urls as $file_index => $file_url ) { /** * Allow for override of SSL replacement. * * By default Gravity Forms will attempt to determine if the schema of the URL should be overwritten for SSL. * This is not ideal for all situations, particularly domain mapping. Setting $field_ssl to false will prevent * the override. * * @since 2.1.1.23 * * @param bool $field_ssl True to allow override if needed or false if not. * @param string $file_url The file URL in question. * @param GF_Field_FileUpload $field The field object for further context. */ $field_ssl = apply_filters( 'gform_secure_file_download_is_https', true, $file_url, $this ); if ( GFCommon::is_ssl() && strpos( $file_url, 'http:' ) !== false && $field_ssl === true ) { $file_url = str_replace( 'http:', 'https:', $file_url ); } $download_file_text = esc_attr__( 'Download file', 'gravityforms' ); $delete_file_text = esc_attr__( 'Delete file', 'gravityforms' ); $view_file_text = esc_attr__( 'View file', 'gravityforms' ); $file_index = intval( $file_index ); $file_url = esc_attr( $file_url ); $display_file_url = GFCommon::truncate_url( $file_url ); $file_url = $this->get_download_url( $file_url ); $preview .= "
{$display_file_url}
"; } $preview .= '
'; return $preview; } else { $input_name = "input_{$id}"; $uploaded_files = isset( GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] : array(); $file_infos = $multiple_files ? $uploaded_files : RGFormsModel::get_temp_filename( $form_id, $input_name ); if ( ! empty( $file_infos ) ) { $preview = sprintf( "
", $file_list_id ); $file_infos = $multiple_files ? $uploaded_files : array( $file_infos ); foreach ( $file_infos as $file_info ) { if ( GFCommon::is_legacy_markup_enabled( $form ) ) { $file_upload_markup = "" . esc_attr__( " . esc_html( $file_info['uploaded_filename'] ) . ''; } else { $file_upload_markup = sprintf( '%s', esc_html( $file_info['uploaded_filename'] ) ); // TODO: get file size $file_upload_markup .= sprintf( '%s', esc_html( $file_info['uploaded_filesize'] ) ); $file_upload_markup .= '100%'; $file_upload_markup .= sprintf( '', $form_id, $id, esc_html__( 'Delete this file', 'gravityforms' ), esc_html( $file_info['uploaded_filename'] ) ); } /** * Modify the HTML for the Multi-File Upload "preview." * * @since Unknown * * @param string $file_upload_markup The current HTML for the field. * @param array $file_info Details about the file uploaded. * @param int $form_id The current Form ID. * @param int $id The current Field ID. */ $file_upload_markup = apply_filters( 'gform_file_upload_markup', $file_upload_markup, $file_info, $form_id, $id ); $preview .= "
{$file_upload_markup}
"; } $preview .= '
'; if ( ! $multiple_files ) { $upload = str_replace( " class='", " class='gform_hidden ", $upload ); } return "
" . $upload . " {$preview}
"; } else { $preview = $multiple_files ? sprintf( "
", $file_list_id ) : ''; return "
$upload
" . $preview; } } } public function is_value_submission_empty( $form_id ) { $input_name = 'input_' . $this->id; $tmp_path = GFFormsModel::get_upload_path( $form_id ) . '/tmp/'; if ( $this->multipleFiles ) { $uploaded_files = GFFormsModel::$uploaded_files[ $form_id ]; $file_info = rgar( $uploaded_files, $input_name ); if ( empty( $file_info ) ) { return true; } foreach ( $file_info as $key => $file ) { if ( empty( $file['uploaded_filename'] ) ) { $this->unset_uploaded_file( $input_name, $key ); continue; } /* * Allow add-ons and custom code to skip the file validation. * * @since 2.7.4 * * @param bool $skip_validation Whether to skip the file validation. * @param array $file The file information. * @param object $field The current field object. */ if ( ! gf_apply_filters( array( 'gform_validate_required_file_exists', $form_id, $this->id, ), isset( $file['temp_filename'] ), $file, $this ) ) { // Skipping existing file populated by an add-on or custom code. continue; } if ( empty( $file['temp_filename'] ) ) { $this->unset_uploaded_file( $input_name, $key ); continue; } $tmp_file = $tmp_path . wp_basename( $file['temp_filename'] ); if ( ! file_exists( $tmp_file ) ) { $this->unset_uploaded_file( $input_name, $key ); } } return empty( GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] ); } else { $file_info = GFFormsModel::get_temp_filename( $form_id, $input_name ); return ! $file_info && empty( $_FILES[ $input_name ]['name'] ); } } /** * Remove invalid file from the uploaded files array. * * @since 2.7.4 * * @param $input_name * @param $key * * @return void */ public function unset_uploaded_file( $input_name, $key ) { GFCommon::log_debug( __METHOD__ . "(): Removing invalid file for {$input_name} key {$key}." ); unset( GFFormsModel::$uploaded_files[ $this->formId ][ $input_name ][ $key ] ); } public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) { if ( ! $this->multipleFiles ) { return $this->get_single_file_value( $form['id'], $input_name ); } if ( $this->is_entry_detail() && empty( $lead ) ) { // Deleted files remain in the $value from $_POST so use the updated entry value. $lead = GFFormsModel::get_lead( $lead_id ); $value = rgar( $lead, strval( $this->id ) ); } return $this->get_multifile_value( $form['id'], $input_name, $value, $lead_id ); } /** * Get the value of the multifile input. * * @since 2.6.8 Added $entry_id parameter. * * @param int $form_id ID of the form * @param string $input_name Name of the input (input_1) * @param string $value Value of the input * @param int $entry_id ID of the entry * * @return string */ public function get_multifile_value( $form_id, $input_name, $value, $entry_id = null ) { global $_gf_uploaded_files; GFCommon::log_debug( __METHOD__ . '(): Starting.' ); if ( isset( $_gf_uploaded_files[ $input_name ] ) ) { $value = $_gf_uploaded_files[ $input_name ]; } else { if ( isset( GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] ) ) { $uploaded_temp_files = GFFormsModel::$uploaded_files[ $form_id ][ $input_name ]; $uploaded_files = array(); foreach ( $uploaded_temp_files as $i => $file_info ) { // File was previously uploaded to form; do not process temp. if ( ! isset( $file_info['temp_filename'] ) ) { $existing_file = $this->check_existing_entry( $entry_id, $input_name, $file_info ); // We already have the file path in $existing_file, however it's good to check that the file path in the entry meta matches. $uploaded_path = gform_get_meta( $entry_id, self::get_file_upload_path_meta_key_hash( $existing_file ) ); if ( $uploaded_path ) { $uploaded_files[ $i ] = $uploaded_path['url'] . $uploaded_path['file_name']; } continue; } $temp_filepath = GFFormsModel::get_upload_path( $form_id ) . '/tmp/' . wp_basename( $file_info['temp_filename'] ); if ( $file_info && file_exists( $temp_filepath ) ) { $uploaded_files[ $i ] = $this->move_temp_file( $form_id, $file_info ); } } if ( ! empty( $value ) ) { // merge with existing files (admin edit entry) $value = json_decode( $value, true ); $value = array_merge( $value, $uploaded_files ); $value = json_encode( $value ); } else { $value = json_encode( $uploaded_files ); } } else { GFCommon::log_debug( __METHOD__ . '(): No files uploaded. Exiting.' ); $value = ''; } $_gf_uploaded_files[ $input_name ] = $value; } if ( ! GFCommon::is_json( $value ) ) { $value = $this->get_parsed_list_of_files( $value, $form_id, $input_name ); } $value_safe = $this->sanitize_entry_value( $value, $form_id ); return $value_safe; } /** * Check existing entry for the file to re-use its URL rather than recreating as the date may be different. * * @since 2.6.8 * * @param int $entry_id The id of the current entry * @param string $input_name The name of the input field (input_1) * @param array $file_info Array of file details * * @return mixed Array of file details or URL of existing file */ public function check_existing_entry( $entry_id, $input_name, $file_info ) { $existing_entry = $entry_id ? GFAPI::get_entry( $entry_id ) : null; if ( ! $existing_entry || is_wp_error( $existing_entry ) ) { return $file_info; } $input_id = str_replace( 'input_', '', $input_name ); $existing_files = GFCommon::maybe_decode_json( rgar( $existing_entry, $input_id ) ); $existing_file_url = null; if ( ! is_array( $existing_files ) ) { return $file_info; } foreach ( $existing_files as $existing_file ) { $existing_file_pathinfo = pathinfo( $existing_file ); if ( $file_info['uploaded_filename'] === $existing_file_pathinfo['basename'] ) { $existing_file_url = $existing_file; break; } } if ( $existing_file_url ) { $file_info = $existing_file_url; } return $file_info; } /** * Given the comma-delimited string of file paths, get the JSON array representing * any which still exist (i.e., haven't been deleted using the UI). * * @since 2.5.8 * * @param string $value A comma-delimited list of file paths. * @param int $form_id The form ID for this entry. * @param string $input_name The input name holding the current list of files. * * @return false|string */ public function get_parsed_list_of_files( $value, $form_id, $input_name ) { $parts = explode( ',', $value ); $uploaded = rgars( GFFormsModel::$uploaded_files, $form_id . '/' . $input_name, array() ); $uploaded = wp_list_pluck( $uploaded, 'uploaded_filename' ); $parts = array_filter( $parts, function ( $part ) use ( $uploaded ) { $basename = wp_basename( trim( $part ) ); return in_array( $basename, $uploaded, true ); } ); return wp_json_encode( $parts ); } public function get_single_file_value( $form_id, $input_name ) { global $_gf_uploaded_files; GFCommon::log_debug( __METHOD__ . '(): Starting.' ); if ( empty( $_gf_uploaded_files ) ) { $_gf_uploaded_files = array(); } if ( ! isset( $_gf_uploaded_files[ $input_name ] ) ) { //check if file has already been uploaded by previous step $file_info = GFFormsModel::get_temp_filename( $form_id, $input_name ); $temp_filename = rgar( $file_info, 'temp_filename', '' ); $temp_filepath = GFFormsModel::get_upload_path( $form_id ) . '/tmp/' . $temp_filename; if ( $file_info && file_exists( $temp_filepath ) ) { GFCommon::log_debug( __METHOD__ . '(): File already uploaded to tmp folder, moving.' ); $_gf_uploaded_files[ $input_name ] = $this->move_temp_file( $form_id, $file_info ); } else if ( ! empty( $_FILES[ $input_name ]['name'] ) ) { GFCommon::log_debug( __METHOD__ . '(): calling upload_file' ); $_gf_uploaded_files[ $input_name ] = $this->upload_file( $form_id, $_FILES[ $input_name ] ); } else { GFCommon::log_debug( __METHOD__ . '(): No file uploaded. Exiting.' ); } } return rgget( $input_name, $_gf_uploaded_files ); } public function upload_file( $form_id, $file ) { GFCommon::log_debug( __METHOD__ . '(): Uploading file: ' . $file['name'] ); $target = GFFormsModel::get_file_upload_path( $form_id, $file['name'] ); if ( ! $target ) { GFCommon::log_debug( __METHOD__ . '(): FAILED (Upload folder could not be created.)' ); return 'FAILED (Upload folder could not be created.)'; } GFCommon::log_debug( __METHOD__ . '(): Upload folder is ' . print_r( $target, true ) ); if ( move_uploaded_file( $file['tmp_name'], $target['path'] ) ) { GFCommon::log_debug( __METHOD__ . '(): File ' . $file['tmp_name'] . ' successfully moved to ' . $target['path'] . '.' ); $this->set_permissions( $target['path'] ); return $target['url']; } else { GFCommon::log_debug( __METHOD__ . '(): FAILED (Temporary file ' . $file['tmp_name'] . ' could not be copied to ' . $target['path'] . '.)' ); return 'FAILED (Temporary file could not be copied.)'; } } public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) { if ( $this->multipleFiles ) { $uploaded_files_arr = empty( $value ) ? array() : json_decode( $value, true ); $file_count = count( $uploaded_files_arr ); if ( $file_count > 1 ) { $value = empty( $uploaded_files_arr ) ? '' : sprintf( esc_html__( '%d files', 'gravityforms' ), count( $uploaded_files_arr ) ); return $value; } elseif ( $file_count == 1 ) { $value = current( $uploaded_files_arr ); } elseif ( $file_count == 0 ) { return; } } $file_path = $value; if ( ! empty( $file_path ) ) { //displaying thumbnail (if file is an image) or an icon based on the extension $thumb = GFEntryList::get_icon_url( $file_path ); $file_path = $this->get_download_url( $file_path ); $file_path = esc_attr( $file_path ); $value = ""; } return $value; } public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) { if ( empty( $value ) ) { return ''; } $output = ''; $output_arr = array(); $files = json_decode( $value, true ); if ( ! is_array( $files ) ) { $files = array( $value ); } $force_download = in_array( 'download', $this->get_modifiers() ); if ( is_array( $files ) ) { foreach ( $files as $file_path ) { if ( is_array( $file_path ) ) { $basename = rgar( $file_path, 'uploaded_name' ); $file_path = rgar( $file_path, 'tmp_url' ); } else { $basename = wp_basename( $file_path ); } $file_path = $this->get_download_url( $file_path, $force_download ); /** * Allow for override of SSL replacement * * By default Gravity Forms will attempt to determine if the schema of the URL should be overwritten for SSL. * This is not ideal for all situations, particularly domain mapping. Setting $field_ssl to false will prevent * the override. * * @since 2.1.1.23 * * @param bool $field_ssl True to allow override if needed or false if not. * @param string $file_path The file path of the download file. * @param GF_Field_FileUpload $field The field object for further context. */ $field_ssl = apply_filters( 'gform_secure_file_download_is_https', true, $file_path, $this ); if ( GFCommon::is_ssl() && strpos( $file_path, 'http:' ) !== false && $field_ssl === true ) { $file_path = str_replace( 'http:', 'https:', $file_path ); } /** * Allows for the filtering of the file path before output. * * @since 2.1.1.23 * * @param string $file_path The file path of the download file. * @param GF_Field_FileUpload $field The field object for further context. */ $file_path = str_replace( ' ', '%20', apply_filters( 'gform_fileupload_entry_value_file_path', $file_path, $this ) ); $output_arr[] = $format == 'text' ? $file_path : sprintf( "
  • %s
  • ", esc_attr( $file_path ), esc_attr__( 'Click to view', 'gravityforms' ), $basename ); } $output = join( PHP_EOL, $output_arr ); } return empty( $output ) || $format == 'text' ? $output : sprintf( '', $output ); } /** * Gets merge tag values. * * @since Unknown * @access public * * @uses GF_Field::get_modifiers() * @uses GF_Field_FileUpload::get_download_url() * * @param array|string $value The value of the input. * @param string $input_id The input ID to use. * @param array $entry The Entry Object. * @param array $form The Form Object * @param string $modifier The modifier passed. * @param array|string $raw_value The raw value of the input. * @param bool $url_encode If the result should be URL encoded. * @param bool $esc_html If the HTML should be escaped. * @param string $format The format that the value should be. * @param bool $nl2br If the nl2br function should be used. * * @return string The processed merge tag. */ public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) { if ( empty( $raw_value ) ) { return ''; } $force_download = in_array( 'download', $this->get_modifiers() ); $files = json_decode( $raw_value, true ); if ( ! is_array( $files ) ) { $files = array( $raw_value ); } foreach ( $files as &$file ) { if ( is_array( $file ) ) { $file = rgar( $file, 'tmp_url' ); } $file = str_replace( ' ', '%20', $this->get_download_url( $file, $force_download ) ); if ( $esc_html ) { $file = esc_html( $file ); } } $value = $format == 'html' ? join( '
    ', $files ) : join( ', ', $files ); if ( $url_encode ) { $value = urlencode( $value ); } return $value; } public function move_temp_file( $form_id, $tempfile_info ) { $target = GFFormsModel::get_file_upload_path( $form_id, $tempfile_info['uploaded_filename'] ); $source = GFFormsModel::get_upload_path( $form_id ) . '/tmp/' . wp_basename( $tempfile_info['temp_filename'] ); GFCommon::log_debug( __METHOD__ . '(): Moving temp file from: ' . $source ); if ( rename( $source, $target['path'] ) ) { GFCommon::log_debug( __METHOD__ . '(): File successfully moved.' ); $this->set_permissions( $target['path'] ); return $target['url']; } else { GFCommon::log_debug( __METHOD__ . '(): FAILED (Temporary file could not be moved.)' ); return 'FAILED (Temporary file could not be moved.)'; } } function set_permissions( $path ) { GFCommon::log_debug( __METHOD__ . '(): Setting permissions on: ' . $path ); GFFormsModel::set_permissions( $path ); } public function sanitize_settings() { parent::sanitize_settings(); if ( $this->maxFileSize ) { $this->maxFileSize = absint( $this->maxFileSize ); } if ( $this->maxFiles ) { $this->maxFiles = preg_replace( '/[^0-9,.]/', '', $this->maxFiles ); } $this->multipleFiles = (bool) $this->multipleFiles; $this->allowedExtensions = sanitize_text_field( $this->allowedExtensions ); } public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) { if ( empty( $input_id ) ) { $input_id = $this->id; } $value = rgar( $entry, $input_id ); if ( $this->multipleFiles && ! empty( $value ) ) { return implode( ' , ', json_decode( $value, true ) ); } return $value; } /** * Returns the download URL for a file. The URL is not escaped for output. * * @since 2.0 * @access public * * @param string $file The complete file URL. * @param bool $force_download If the download should be forced. Defaults to false. * * @return string */ public function get_download_url( $file, $force_download = false ) { $download_url = $file; $secure_download_location = true; /** * By default the real location of the uploaded file will be hidden and the download URL will be generated with * a security token to prevent guessing or enumeration attacks to discover the location of other files. * * Return FALSE to display the real location. * * @param bool $secure_download_location If the secure location should be used. Defaults to true. * @param string $file The URL of the file. * @param GF_Field_FileUpload $this The Field */ $secure_download_location = apply_filters( 'gform_secure_file_download_location', $secure_download_location, $file, $this ); $secure_download_location = apply_filters( 'gform_secure_file_download_location_' . $this->formId, $secure_download_location, $file, $this ); if ( ! $secure_download_location ) { /** * Allow filtering of the download URL. * * Allows for manual filtering of the download URL to handle conditions such as * unusual domain mapping and others. * * @since 2.1.1.1 * * @param string $download_url The URL from which to download the file. * @param GF_Field_FileUpload $field The field object for further context. */ return apply_filters( 'gform_secure_file_download_url', $download_url, $this ); } $upload_root = GFFormsModel::get_upload_url( $this->formId ); $upload_root = trailingslashit( $upload_root ); // Only hide the real URL if the location of the file is in the upload root for the form. // The upload root is calculated using the WP Salts so if the WP Salts have changed then file can't be located during the download request. if ( strpos( $file, $upload_root ) !== false ) { $file = str_replace( $upload_root, '', $file ); $download_url = site_url( 'index.php' ); $args = array( 'gf-download' => urlencode( $file ), 'form-id' => $this->formId, 'field-id' => $this->id, 'hash' => GFCommon::generate_download_hash( $this->formId, $this->id, $file ), ); if ( $force_download ) { $args['dl'] = 1; } $download_url = add_query_arg( $args, $download_url ); } /** * Allow filtering of the download URL. * * Allows for manual filtering of the download URL to handle conditions such as * unusual domain mapping and others. * * @param string $download_url The URL from which to download the file. * @param GF_Field_FileUpload $field The field object for further context. */ return apply_filters( 'gform_secure_file_download_url', $download_url, $this ); } /** * Stores the physical file paths as extra entry meta data. * * @since 2.5.16 * * @param array $form The form object being saved. * @param array $entry The entry object being saved. * * @return array The array that contains the file URLs and their corresponding physical paths. */ public function get_extra_entry_metadata( $form, $entry ) { $value = $entry[ $this->id ]; if ( empty( $value ) ) { return array(); } $file_values = array(); $extra_meta = array(); if ( $this->multipleFiles && ! empty( $value ) ) { $file_values = json_decode( $value, true ); } else { $file_values = array( $value ); } foreach ( $file_values as $file_value ) { if ( is_array( $file_value ) ) { continue; } // If file already has a stored path, skip it. $stored_path_info = gform_get_meta( rgar( $entry, 'id' ), self::get_file_upload_path_meta_key_hash( $file_value ) ); if ( ! empty( $stored_path_info ) ) { continue; }; // Use the filtered path to get the actual file path. $upload_root_info = self::get_upload_root_info( rgar( $form, 'id' ) ); // Default upload path to fall back to. $default_upload_root_info = self::get_default_upload_roots( rgar( $form, 'id' ) ); $url = rgar( $upload_root_info, 'url', $default_upload_root_info['url'] ); $path = rgar( $upload_root_info, 'path', $default_upload_root_info['path'] ); $file_path_info = array( 'path' => $path, 'url' => $url, 'file_name' => wp_basename( $file_value ), ); $file_url_hash = self::get_file_upload_path_meta_key_hash( $file_value ); $extra_meta[ $file_url_hash ] = $file_path_info; } return $extra_meta; } /** * Gets a hash of the file URL to be used as the meta key when saving the file physical path to the entry meta. * * @since 2.5.16 * * @param string $file_url The file URL to generate the hash for. * * @return string */ public static function get_file_upload_path_meta_key_hash( $file_url ) { return substr( hash( 'sha512', $file_url ), 0, 254 ); } } GF_Fields::register( new GF_Field_FileUpload() );