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