max_upload_size = wp_max_upload_size(); add_action( 'wp_ajax_wmufs_chunker', array( $this, 'wmufs_ajax_chunk_receiver' ) ); add_filter( 'plupload_init', array( $this, 'wmufs_filter_plupload_settings' ) ); add_filter( 'plupload_default_settings', array( $this, 'wmufs_filter_plupload_settings' ) ); add_filter( 'plupload_default_params', array( $this, 'wmufs_filter_plupload_params' ) ); add_filter( 'upload_post_params', array( $this, 'wmufs_filter_plupload_params' ) ); } /** * @param $plupload_params * @return mixed */ public function wmufs_filter_plupload_params( $plupload_params ) { $plupload_params['action'] = 'wmufs_chunker'; return $plupload_params; } /** * AJAX chunk receiver. * * Ajax callback for plupload to handle chunked uploads. * Based on code by Davit Barbakadze * https://gist.github.com/jayarjo/5846636 * * Mirrors /wp-admin/async-upload.php * */ public function wmufs_ajax_chunk_receiver() { /** Check that we have an upload and there are no errors. */ if ( empty( $_FILES ) || $_FILES['async-upload']['error'] ) { /** Failed to move uploaded file. */ die(); } /** Authenticate user. */ if ( ! is_user_logged_in() || ! current_user_can( 'upload_files' ) ) { wp_die( __( 'Sorry, you are not allowed to upload files.' ) ); } check_admin_referer( 'media-form' ); /** Check and get file chunks. */ $chunk = isset( $_REQUEST['chunk'] ) ? intval( $_REQUEST['chunk'] ) : 0; //zero index $current_part = $chunk + 1; $chunks = isset( $_REQUEST['chunks'] ) ? intval( $_REQUEST['chunks'] ) : 0; /** Get file name and path + name. */ $fileName = isset( $_REQUEST['name'] ) ? $_REQUEST['name'] : $_FILES['async-upload']['name']; $wmufs_temp_dir = apply_filters( 'wmufs_temp_dir', WP_CONTENT_DIR . '/wmufs-temp' ); //only run on first chunk if ( $chunk === 0 ) { // Create temp directory if it doesn't exist if ( ! @is_dir( $wmufs_temp_dir ) ) { wp_mkdir_p( $wmufs_temp_dir ); } // Protect temp directory from browsing. $index_pathname = $wmufs_temp_dir . '/index.php'; if ( ! file_exists( $index_pathname ) ) { $file = fopen( $index_pathname, 'w' ); if ( false !== $file ) { fwrite( $file, "get_upload_limit(); if ( file_exists( $filePath ) && filesize( $filePath ) + filesize( $_FILES['async-upload']['tmp_name'] ) > $wmufs_max_upload_size ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( "WMUFS: File size limit exceeded." ); } if ( ! $chunks || $chunk == $chunks - 1 ) { @unlink( $filePath ); if ( ! isset( $_REQUEST['short'] ) || ! isset( $_REQUEST['type'] ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'The file size has exceeded the maximum file size setting.', 'tuxedo-big-file-uploads' ), 'filename' => $fileName, ), ) ); wp_die(); } else { status_header( 202 ); printf( '
%s %s
%s
', sprintf( '', __( 'Dismiss' ) ), sprintf( /* translators: %s: Name of the file that failed to upload. */ __( '“%s” has failed to upload.' ), esc_html( $fileName ) ), __( 'The file size has exceeded the maximum file size setting.', 'tuxedo-big-file-uploads' ) ); exit; } } die(); } /** Open temp file. */ if ( $chunk == 0 ) { $out = @fopen( $filePath, 'wb'); } elseif ( is_writable( $filePath ) ) { // $out = @fopen( $filePath, 'ab' ); } else { $out = false; } if ( $out ) { /** Read binary input stream and append it to temp file. */ $in = @fopen( $_FILES['async-upload']['tmp_name'], 'rb' ); if ( $in ) { while ( $buff = fread( $in, 4096 ) ) { fwrite( $out, $buff ); } } else { /** Failed to open input stream. */ /** Attempt to clean up unfinished output. */ @fclose( $out ); @unlink( $filePath ); error_log( "WMUFS: Error reading uploaded part $current_part of $chunks." ); if ( ! isset( $_REQUEST['short'] ) || ! isset( $_REQUEST['type'] ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => sprintf( __( 'There was an error reading uploaded part %1$d of %2$d.', 'tuxedo-big-file-uploads' ), $current_part, $chunks ), 'filename' => esc_html( $fileName ), ), ) ); wp_die(); } else { status_header( 202 ); printf( '
%s %s
%s
', sprintf( '', __( 'Dismiss' ) ), sprintf( /* translators: %s: Name of the file that failed to upload. */ __( '“%s” has failed to upload.' ), esc_html( $fileName ) ), sprintf( __( 'There was an error reading uploaded part %1$d of %2$d.', 'tuxedo-big-file-uploads' ), $current_part, $chunks ) ); exit; } } @fclose( $in ); @fclose( $out ); @unlink( $_FILES['async-upload']['tmp_name'] ); } else { /** Failed to open output stream. */ error_log( "BFU: Failed to open output stream $filePath to write part $current_part of $chunks." ); if ( ! isset( $_REQUEST['short'] ) || ! isset( $_REQUEST['type'] ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => sprintf( __( 'There was an error opening the temp file %s for writing. Available temp directory space may be exceeded or the temp file was cleaned up before the upload completed.', 'tuxedo-big-file-uploads' ), esc_html( $filePath ) ), 'filename' => esc_html( $fileName ), ), ) ); wp_die(); } else { status_header( 202 ); printf( '
%s %s
%s
', sprintf( '', __( 'Dismiss' ) ), sprintf( /* translators: %s: Name of the file that failed to upload. */ __( '“%s” has failed to upload.' ), esc_html( $fileName ) ), sprintf( __( 'There was an error opening the temp file %s for writing. Available temp directory space may be exceeded or the temp file was cleaned up before the upload completed.', 'tuxedo-big-file-uploads' ), esc_html( $filePath ) ) ); exit; } } /** Check if file has finished uploading all parts. */ if ( ! $chunks || $chunk == $chunks - 1 ) { //debugging if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { $size = file_exists( $filePath ) ? size_format( filesize( $filePath ), 3 ) : '0 B'; error_log( "BFU: Completing \"$fileName\" upload with a $size final size." ); } /** Recreate upload in $_FILES global and pass off to WordPress. */ $_FILES['async-upload']['tmp_name'] = $filePath; $_FILES['async-upload']['name'] = $fileName; $_FILES['async-upload']['size'] = filesize( $_FILES['async-upload']['tmp_name'] ); $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'] ); $_FILES['async-upload']['type'] = $wp_filetype['type']; header( 'Content-Type: text/plain; charset=' . get_option( 'blog_charset' ) ); if ( ! isset( $_REQUEST['short'] ) || ! isset( $_REQUEST['type'] ) ) { //ajax like media uploader in modal // Compatibility with Easy Digital Downloads plugin. if ( function_exists( 'edd_change_downloads_upload_dir' ) ) { global $pagenow; $pagenow = 'async-upload.php'; edd_change_downloads_upload_dir(); } send_nosniff_header(); nocache_headers(); $this->wp_ajax_upload_attachment(); die( '0' ); } else { //non-ajax like add new media page $post_id = 0; if ( isset( $_REQUEST['post_id'] ) ) { $post_id = absint( $_REQUEST['post_id'] ); if ( ! get_post( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) $post_id = 0; } $id = media_handle_upload( 'async-upload', $post_id, [], [ 'action' => 'wp_handle_sideload', 'test_form' => false, ] ); if ( is_wp_error( $id ) ) { printf( '
%s %s
%s
', sprintf( '', __( 'Dismiss' ) ), sprintf( /* translators: %s: Name of the file that failed to upload. */ __( '“%s” has failed to upload.' ), esc_html( $_FILES['async-upload']['name'] ) ), esc_html( $id->get_error_message() ) ); exit; } if ( $_REQUEST['short'] ) { // Short form response - attachment ID only. echo $id; } else { // Long form response - big chunk of HTML. $type = $_REQUEST['type']; /** * Filters the returned ID of an uploaded attachment. * * The dynamic portion of the hook name, `$type`, refers to the attachment type. * * Possible hook names include: * * - `async_upload_audio` * - `async_upload_file` * - `async_upload_image` * - `async_upload_video` * * @since 1.1.0 * * @param int $id Uploaded attachment ID. */ echo apply_filters( "async_upload_{$type}", $id ); } } } die(); } /** * Return the maximum upload limit in bytes for the current user. * * @since 1.1.0 * * @return integer */ function get_upload_limit() { $max_size = (int) get_option('max_file_size'); if ( ! $max_size ) { $max_size = wp_max_upload_size(); } return $max_size; } /** * Copied from wp-admin/includes/ajax-actions.php because we have to override the args for * the media_handle_upload function. As of WP 6.0.1 */ function wp_ajax_upload_attachment() { check_ajax_referer( 'media-form' ); /* * This function does not use wp_send_json_success() / wp_send_json_error() * as the html4 Plupload handler requires a text/html content-type for older IE. * See https://core.trac.wordpress.org/ticket/31037 */ if ( ! current_user_can( 'upload_files' ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'Sorry, you are not allowed to upload files.' ), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } if ( isset( $_REQUEST['post_id'] ) ) { $post_id = $_REQUEST['post_id']; if ( ! current_user_can( 'edit_post', $post_id ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'Sorry, you are not allowed to attach files to this post.' ), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } } else { $post_id = null; } $post_data = ! empty( $_REQUEST['post_data'] ) ? _wp_get_allowed_postdata( _wp_translate_postdata( false, (array) $_REQUEST['post_data'] ) ) : array(); if ( is_wp_error( $post_data ) ) { wp_die( $post_data->get_error_message() ); } // If the context is custom header or background, make sure the uploaded file is an image. if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ), true ) ) { $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'] ); if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'The uploaded file is not a valid image. Please try again.' ), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } } //this is the modded function from wp-admin/includes/ajax-actions.php $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data, [ 'action' => 'wp_handle_sideload', 'test_form' => false, ] ); if ( is_wp_error( $attachment_id ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => $attachment_id->get_error_message(), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) { if ( 'custom-background' === $post_data['context'] ) { update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] ); } if ( 'custom-header' === $post_data['context'] ) { update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] ); } } $attachment = wp_prepare_attachment_for_js( $attachment_id ); if ( ! $attachment ) { wp_die(); } echo wp_json_encode( array( 'success' => true, 'data' => $attachment, ) ); wp_die(); } /** * Filter plupload settings. * * @since 1.1.0 */ public function wmufs_filter_plupload_settings( $plupload_settings ) { $max_chunk = ( MB_IN_BYTES * 20 ); //20MB max chunk size (to avoid timeouts) if ( $max_chunk > $this->max_upload_size ) { $default_chunk = ( $this->max_upload_size * 0.8 ) / KB_IN_BYTES; } else { $default_chunk = $max_chunk / KB_IN_BYTES; } //define( 'WMUFS_FILE_UPLOADS_CHUNK_SIZE_KB', 512 );//TODO remove if ( ! defined( 'WMUFS_FILE_UPLOADS_CHUNK_SIZE_KB' ) ) { define( 'WMUFS_FILE_UPLOADS_CHUNK_SIZE_KB', $default_chunk ); } if ( ! defined( 'WMUFS_FILE_UPLOADS_RETRIES' ) ) { define( 'WMUFS_FILE_UPLOADS_RETRIES', 1 ); } $plupload_settings['url'] = admin_url( 'admin-ajax.php' ); $plupload_settings['filters']['max_file_size'] = $this->get_upload_limit( '' ) . 'b'; $plupload_settings['chunk_size'] = WMUFS_FILE_UPLOADS_CHUNK_SIZE_KB . 'kb'; $plupload_settings['max_retries'] = WMUFS_FILE_UPLOADS_RETRIES; return $plupload_settings; } } WMUFS_File_Chunk::get_instance()->init();