Upload()
Description #
AnsPress Upload type field object.
Source #
File: lib/form/class-upload.php
class Upload extends \AnsPress\Form\Field { /** * The field type. * * @var string */ public $type = 'upload'; /** * Is multiple upload field. * * @var boolean */ public $multiple_upload = false; /** * Is files uploaded. * * @var boolean * @since 4.1.0 */ public $uploaded = false; /** * The uploaded files. * * @var boolean|array */ public $uploaded_files = false; /** * Check weather this is async upload. * * @var boolean */ public $async_upload = false; /** * Initialize the class. * * @param string $form_name Name of parent form. * @param string $name Name of field. * @param array $args Field arguments. */ public function __construct( $form_name, $name, $args ) { parent::__construct( $form_name, $name, $args ); // Do not add array in field name. $this->field_name = $this->id(); $this->multiple_upload = $this->args['upload_options']['multiple']; // Add array to field name if multiple file allowed. if ( true === $this->multiple_upload ) { $this->field_name = $this->field_name . '[]'; } // Make sure field is sanitized. $this->sanitize_cb = array_merge( array( 'upload' ), $this->sanitize_cb ); $this->validate_cb = array_merge( array( 'upload' ), $this->validate_cb ); } /** * Prepare field. * * @return void */ protected function prepare() { $this->args = wp_parse_args( $this->args, array( 'label' => __( 'AnsPress Upload Field', 'anspress-question-answer' ), 'upload_options' => array(), 'browse_label' => __( 'Select file(s) to upload', 'anspress-question-answer' ), ) ); $this->args['upload_options'] = wp_parse_args( $this->args['upload_options'], array( 'allowed_mimes' => ap_allowed_mimes(), 'max_files' => 1, 'multiple' => false, 'label_deny_type' => __( 'This file type is not allowed to upload.', 'anspress-question-answer' ), 'async_upload' => false, ) ); if ( ! isset( $this->args['upload_options']['label_max_added'] ) ) { $this->args['upload_options']['label_max_added'] = sprintf( // Translators: %d contains maximum files allowed to upload. __( 'You cannot add more then %d files', 'anspress-question-answer' ), $this->args['upload_options']['max_files'] ); } // Call parent prepare(). parent::prepare(); } /** * Order of HTML markup. * * @return void * @since 4.1.8 */ protected function html_order() { if ( empty( $this->args['output_order'] ) ) { $this->output_order = array( 'wrapper_start', 'label', 'field_wrap_start', 'errors', 'field_markup', 'desc', 'file_list', 'field_wrap_end', 'wrapper_end' ); } else { $this->output_order = $this->args['output_order']; } } /** * Arguments passed to sanitization callback. * * @param mixed $val Value to sanitize. * @return array */ protected function sanitize_cb_args( $val ) { return array( $val, $this->get( 'upload_options' ) ); } /** * Format $_FILES field which have multiple files. * * @param array $file_post single $_FILE field. * @return array */ private function format_multiple_files( $file_post ) { if ( ! is_array( $file_post['name'] ) ) { return $file_post; } $file_ary = array(); $file_count = count( $file_post['name'] ); $file_keys = array_keys( $file_post ); for ( $i = 0; $i < $file_count; $i++ ) { foreach ( $file_keys as $key ) { $file_ary[ $i ][ $key ] = $file_post[ $key ][ $i ]; } } return array_filter( $file_ary, function ( $a ) { return ! empty( $a['name'] ); } ); } /** * Get POST (unsafe) value of a field. * * @return mixed */ public function unsafe_value() { $request_value = $this->get( $this->id( $this->field_name ), null, $_FILES ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( $request_value ) { return $this->format_multiple_files( $request_value ); } } /** * Show the list of previously attached media. * * @return void * @since 4.1.8 */ public function file_list() { $medias = get_posts( array( 'post_type' => 'attachment', 'title' => '_ap_temp_media', 'post_author' => get_current_user_id(), ) ); // Show temporary images uploaded. $this->add_html( '<div class="ap-upload-list">' ); if ( $medias ) { foreach ( $medias as $media ) { $this->add_html( '<div><span class="ext">' . pathinfo( $media->guid, PATHINFO_EXTENSION ) . '</span>' . basename( $media->guid ) . '<span class="size">' . size_format( filesize( get_attached_file( $media->ID ) ), 2 ) . '</span></div>' ); } } $this->add_html( '</div>' ); } /** * Return arguments for used by JS. * * @return string JSON * @since 4.1.8 */ public function js_args() { $args = $this->get( 'upload_options' ); $allowed_ext = '.' . str_replace( '|', ',.', implode( ',.', array_keys( $args['allowed_mimes'] ) ) ); unset( $args['allowed_mimes'] ); $args['field_name'] = $this->original_name; $args['form_name'] = $this->form_name; return wp_json_encode( $args ); } /** * Field markup. * * @return void */ public function field_markup() { parent::field_markup(); $args = $this->get( 'upload_options' ); $allowed_ext = '.' . str_replace( '|', ',.', implode( ',.', array_keys( $args['allowed_mimes'] ) ) ); $this->add_html( '<div class="ap-upload-c">' ); $this->add_html( '<input type="file"' ); $this->add_html( 'data-upload="' . esc_js( $this->js_args() ) . '"' . $this->common_attr() ); $this->add_html( $this->custom_attr() ); $this->add_html( $args['multiple'] ? ' multiple="multiple"' : '' ); $this->add_html( ' accept="' . esc_attr( $allowed_ext ) . '" ' ); $this->add_html( ' />' ); $this->add_html( '</div>' ); /** This action is documented in lib/form/class-input.php */ do_action_ref_array( 'ap_after_field_markup', array( &$this ) ); } /** * Replace all dummy images found in editor type field. * * @param string $str Content where to replace images. * @param array|false $allowed_files Pass array of allowed file names . * @return string Replaced string. */ public function replace_temp_image( $str, $allowed_files = false ) { $allowed_files = array_map( 'sanitize_file_name', $allowed_files ); // Check for allowed files. $new_files = array(); if ( false !== $allowed_files && $this->value() ) { foreach ( $this->value() as $k => $file ) { if ( in_array( $file['name'], $allowed_files, true ) ) { $new_files[] = $file; } } $this->value = $new_files; } if ( false === $this->uploaded ) { $this->save_uploads(); } return preg_replace_callback( '/({{apimage (.*?)}})/', array( $this, 'file_name_search_replace' ), $str ); } /** * Callback for preg replace callback for replacing temporary * images in a string. * * @param array $m Matching tags. * @return string */ private function file_name_search_replace( $m ) { preg_match_all( '/"([^"]*)"/', $m[2], $attrs, PREG_SET_ORDER, 0 ); $sanitized_filename = sanitize_file_name( $attrs[0][1] ); if ( ! empty( $this->uploaded_files[ $sanitized_filename ] ) ) { $url = wp_get_attachment_url( $this->uploaded_files[ $sanitized_filename ] ); $alt = ! empty( $attrs[1][1] ) ? ' alt="' . esc_attr( $attrs[1][1] ) . '"' : ''; return '<img src="' . $url . '"' . $alt . ' />'; } } /** * Upload a file. * * @param array $file File array. * @return void * @since 4.1.0 * @since 4.1.5 Fixed: custom mimes are not working. */ private function upload_file( $file ) { $id = $this->upload( $file ); if ( is_wp_error( $id ) ) { $this->add_error( $id->get_error_code(), $id->get_error_message() ); } else { $this->uploaded_files[ sanitize_file_name( $file['name'] ) ] = $id; } } /** * Save all uploads to server. * * @return void */ public function save_uploads() { if ( $this->have_errors() || true === $this->uploaded ) { return; } $value = $this->value(); // Return if value is empty. if ( empty( $value ) ) { return; } if ( $this->get( 'upload_options.multiple', false ) ) { foreach ( (array) $value as $file ) { $this->upload_file( $file ); } } else { $this->upload_file( $value ); } $this->value = $this->uploaded_files; $this->uploaded = true; } /** * Set post parent of uploaded files. * * @param array $args Array of arguments. * @return void */ public function after_save( $args = array() ) { parent::after_save(); if ( empty( $args ) || empty( $args['post_id'] ) ) { return; } if ( ! empty( $this->uploaded_files ) ) { foreach ( $this->uploaded_files as $id ) { ap_set_media_post_parent( $id, $args['post_id'] ); } } } /** * Upload file and store it in temporary directory. * * Copied directly from WordPress Core. Only difference is upload directory. * All files were uploaded to `anspress-temp` directory and it need to moved * manually. All files older then 2 hours are deleted from `anspress-temp` directory. * * @param array $file $_FILE value. * @return string|WP_Error * @since 4.1.8 */ private function upload( $file ) { // If there is error in file then return. if ( isset( $file['error'] ) && ! is_numeric( $file['error'] ) && $file['error'] ) { return new \WP_Error( 'upload_file_error', $file['error'] ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error']. $upload_error_strings = array( false, __( 'The uploaded file exceeds the upload_max_filesize directive in php.ini.', 'anspress-question-answer' ), __( 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', 'anspress-question-answer' ), __( 'The uploaded file was only partially uploaded.', 'anspress-question-answer' ), __( 'No file was uploaded.', 'anspress-question-answer' ), '', __( 'Missing a temporary folder.', 'anspress-question-answer' ), __( 'Failed to write file to disk.', 'anspress-question-answer' ), __( 'File upload stopped by extension.', 'anspress-question-answer' ), ); // Check error. if ( isset( $file['error'] ) && $file['error'] > 0 ) { return new \WP_Error( 'upload_file_size', $upload_error_strings[ $file['error'] ] ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } $file_size = $file['size']; // A non-empty file will pass this test. if ( ! ( $file_size > 0 ) ) { return new \WP_Error( 'upload_file_size', __( 'File is empty. Please upload something more substantial.', 'anspress-question-answer' ) ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } // Check file size. if ( $file_size > ap_opt( 'max_upload_size' ) ) { return new \WP_Error( 'upload_file_size', __( 'File is bigger than the allowed limit.', 'anspress-question-answer' ) ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } // Check file uploaded using proper method. if ( true !== is_uploaded_file( $file['tmp_name'] ) ) { return new \WP_Error( 'upload_file_failed', __( 'Specified file failed upload test.', 'anspress-question-answer' ) ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } $mimes = $this->get( 'upload_options.allowed_mimes' ); $mimes = ! empty( $mimes ) ? $mimes : false; // A correct MIME type will pass this test. $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect. if ( $proper_filename ) { $file['name'] = $proper_filename; } if ( ! $type || ! $ext ) { return new \WP_Error( 'upload_file_ext', __( 'Sorry, this file type is not permitted for security reasons.', 'anspress-question-answer' ) ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } if ( ! $type ) { $type = $file['type']; } $uploads = wp_upload_dir(); /** * A writable uploads dir will pass this test. */ if ( false !== $uploads['error'] ) { return new \WP_Error( 'upload_file_dir', $uploads['error'] ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } $temp_dir = $uploads['basedir'] . '/anspress-temp/'; // Make sure WP_Filesystem is loaded. if ( ! function_exists( 'WP_Filesystem' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } // Initialize WP_Filesystem. if ( ! WP_Filesystem() ) { // Unable to initialize WP_Filesystem, handle error accordingly. return; } global $wp_filesystem; // Make dir if not exists. if ( ! file_exists( $temp_dir ) ) { $wp_filesystem->mkdir( $temp_dir ); } $sha = sha1_file( $file['tmp_name'] ); $user_id = get_current_user_id(); $new_file_name = "{$sha}_$user_id.$ext"; $new_file = $temp_dir . "$new_file_name"; $move_new_file = move_uploaded_file( $file['tmp_name'], $new_file ); // Return if unable to move file. if ( false === $move_new_file ) { return new \WP_Error( 'upload_file_move', 'The uploaded file could not be moved' ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } // Set correct file permissions. $stat = stat( dirname( $new_file ) ); $perms = $stat['mode'] & 0000666; // Use WP_Filesystem's chmod method. $wp_filesystem->chmod( $new_file, $perms ); return $new_file_name; // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound } /** * Return url of all uploaded files. * * @return array * @since 4.1.8 */ public function get_uploaded_files_url() { if ( true !== $this->uploaded || empty( $this->uploaded_files ) ) { return array(); } $uploads = wp_upload_dir(); $temp_dir = $uploads['baseurl'] . '/anspress-temp/'; $ret = array(); foreach ( $this->uploaded_files as $old => $new ) { $ret[ $old ] = $temp_dir . $new; } return $ret; } }
Expand full source code Collapse full source code View on GitHub: lib/form/class-upload.php:25
Add your comment