Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions src/wp-admin/includes/file.php
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ function validate_file_to_edit( $file, $allowed_files = array() ) {
*
* @access private
* @since 4.0.0
* @since 7.1.0 Added the `$post` parameter.
*
* @see wp_handle_upload_error
*
Expand All @@ -790,8 +791,10 @@ function validate_file_to_edit( $file, $allowed_files = array() ) {
* @type bool $test_type Whether to test that the mime type of the file is as expected.
* @type string[] $mimes Array of allowed mime types keyed by their file extension regex.
* }
* @param string $time Time formatted in 'yyyy/mm'.
* @param string $action Expected value for `$_POST['action']`.
* @param string|null $time Time to use for the upload directory. Accepts a 'yyyy/mm'
* formatted string or a MySQL datetime.
* @param string $action Expected value for `$_POST['action']`.
* @param WP_Post|null $post Optional. Post object the upload is associated with. Default null.
* @return array {
* On success, returns an associative array of file attributes.
* On failure, returns `$overrides['upload_error_handler']( &$file, $message )`
Expand All @@ -802,7 +805,7 @@ function validate_file_to_edit( $file, $allowed_files = array() ) {
* @type string $type Mime type of the newly-uploaded file.
* }
*/
function _wp_handle_upload( &$file, $overrides, $time, $action ) {
function _wp_handle_upload( &$file, $overrides, $time, $action, $post = null ) {
// The default error handler.
if ( ! function_exists( 'wp_handle_upload_error' ) ) {
function wp_handle_upload_error( &$file, $message ) {
Expand Down Expand Up @@ -975,6 +978,23 @@ function wp_handle_upload_error( &$file, $message ) {
$type = '';
}

/**
* Filters the time value used to determine the directory where the file is stored.
*
* The filtered value is passed to wp_upload_dir(), where it is used to determine
* the year/month subdirectory when year/month folders are enabled.
*
* @since 7.1.0
*
* @param string|null $time Time to use for the upload directory. Accepts a 'yyyy/mm'
* formatted string or a MySQL datetime.
* @param WP_Post|null $post Post object the upload is associated with, or null.
* @param array $file Reference to a single element from `$_FILES`.
* @param array|false $overrides An array of override parameters for this file, or boolean false.
* @param string $action Expected value for `$_POST['action']`.
*/
$time = apply_filters( 'wp_handle_upload_time', $time, $post, $file, $overrides, $action );

/*
* A writable uploads dir will pass this test. Again, there's no point
* overriding this one.
Expand Down Expand Up @@ -1083,6 +1103,7 @@ function wp_handle_upload_error( &$file, $message ) {
* Passes the {@see 'wp_handle_upload'} action.
*
* @since 2.0.0
* @since 7.1.0 Added the `$post` parameter.
*
* @see _wp_handle_upload()
*
Expand All @@ -1092,16 +1113,18 @@ function wp_handle_upload_error( &$file, $message ) {
* @param array|false $overrides Optional. An associative array of names => values
* to override default variables. Default false.
* See _wp_handle_upload() for accepted values.
* @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null.
* @param string|null $time Optional. Time to use for the upload directory. Accepts a 'yyyy/mm'
* formatted string or a MySQL datetime. Default null.
* @param WP_Post|null $post Optional. Post object the upload is associated with. Default null.
* @return array See _wp_handle_upload() for return value.
*/
function wp_handle_upload( &$file, $overrides = false, $time = null ) {
function wp_handle_upload( &$file, $overrides = false, $time = null, $post = null ) {
/*
* $_POST['action'] must be set and its value must equal $overrides['action']
* or this:
*/
$action = $overrides['action'] ?? 'wp_handle_upload';
return _wp_handle_upload( $file, $overrides, $time, $action );
return _wp_handle_upload( $file, $overrides, $time, $action, $post );
}

/**
Expand All @@ -1110,6 +1133,7 @@ function wp_handle_upload( &$file, $overrides = false, $time = null ) {
* Passes the {@see 'wp_handle_sideload'} action.
*
* @since 2.6.0
* @since 7.1.0 Added the `$post` parameter.
*
* @see _wp_handle_upload()
*
Expand All @@ -1119,16 +1143,18 @@ function wp_handle_upload( &$file, $overrides = false, $time = null ) {
* @param array|false $overrides Optional. An associative array of names => values
* to override default variables. Default false.
* See _wp_handle_upload() for accepted values.
* @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null.
* @param string|null $time Optional. Time to use for the upload directory. Accepts a 'yyyy/mm'
* formatted string or a MySQL datetime. Default null.
* @param WP_Post|null $post Optional. Post object the upload is associated with. Default null.
* @return array See _wp_handle_upload() for return value.
*/
function wp_handle_sideload( &$file, $overrides = false, $time = null ) {
function wp_handle_sideload( &$file, $overrides = false, $time = null, $post = null ) {
/*
* $_POST['action'] must be set and its value must equal $overrides['action']
* or this:
*/
$action = $overrides['action'] ?? 'wp_handle_sideload';
return _wp_handle_upload( $file, $overrides, $time, $action );
return _wp_handle_upload( $file, $overrides, $time, $action, $post );
}

/**
Expand Down
10 changes: 8 additions & 2 deletions src/wp-admin/includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid
}
}

$file = wp_handle_upload( $_FILES[ $file_id ], $overrides, $time );
$file = wp_handle_upload( $_FILES[ $file_id ], $overrides, $time, $post );

if ( isset( $file['error'] ) ) {
return new WP_Error( 'upload_error', $file['error'] );
Expand Down Expand Up @@ -465,8 +465,14 @@ function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrid
function media_handle_sideload( $file_array, $post_id = 0, $desc = null, $post_data = array() ) {
$overrides = array( 'test_form' => false );

$post = null;

if ( isset( $post_data['post_date'] ) && substr( $post_data['post_date'], 0, 4 ) > 0 ) {
$time = $post_data['post_date'];

if ( $post_id ) {
$post = get_post( $post_id );
}
} else {
$post = get_post( $post_id );
if ( $post && substr( $post->post_date, 0, 4 ) > 0 ) {
Expand All @@ -476,7 +482,7 @@ function media_handle_sideload( $file_array, $post_id = 0, $desc = null, $post_d
}
}

$file = wp_handle_sideload( $file_array, $overrides, $time );
$file = wp_handle_sideload( $file_array, $overrides, $time, $post );

if ( isset( $file['error'] ) ) {
return new WP_Error( 'upload_error', $file['error'] );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
*/
protected $allow_batch = false;

/**
* Post object associated with the current upload.
*
* @since 7.1.0
* @var WP_Post|null
*/
private $upload_post = null;

/**
* Registers the routes for attachments.
*
Expand Down Expand Up @@ -436,6 +444,7 @@ protected function insert_attachment( $request ) {
$headers = $request->get_headers();

$time = null;
$post = null;

// Matches logic in media_handle_upload().
if ( ! empty( $request['post'] ) ) {
Expand All @@ -446,12 +455,16 @@ protected function insert_attachment( $request ) {
}
}

$this->upload_post = $post;

if ( ! empty( $files ) ) {
$file = $this->upload_from_file( $files, $headers, $time );
} else {
$file = $this->upload_from_data( $request->get_body(), $headers, $time );
}

$this->upload_post = null;

if ( is_wp_error( $file ) ) {
return $file;
}
Expand Down Expand Up @@ -1479,7 +1492,7 @@ protected function upload_from_data( $data, $headers, $time = null ) {
'test_form' => false,
);

$sideloaded = wp_handle_sideload( $file_data, $overrides, $time );
$sideloaded = wp_handle_sideload( $file_data, $overrides, $time, $this->upload_post );

if ( isset( $sideloaded['error'] ) ) {
@unlink( $tmpfname );
Expand Down Expand Up @@ -1653,7 +1666,7 @@ protected function upload_from_file( $files, $headers, $time = null ) {
// Include filesystem functions to get access to wp_handle_upload().
require_once ABSPATH . 'wp-admin/includes/file.php';

$file = wp_handle_upload( $files['file'], $overrides, $time );
$file = wp_handle_upload( $files['file'], $overrides, $time, $this->upload_post );

if ( isset( $file['error'] ) ) {
return new WP_Error(
Expand Down Expand Up @@ -2064,12 +2077,16 @@ public function sideload_item( WP_REST_Request $request ) {
$time = $parent_post->post_date;
}

$this->upload_post = $parent_post;

if ( ! empty( $files ) ) {
$file = $this->upload_from_file( $files, $headers, $time );
} else {
$file = $this->upload_from_data( $request->get_body(), $headers, $time );
}

$this->upload_post = null;

remove_filter( 'wp_unique_filename', $filter_filename );
remove_filter( 'image_editor_output_format', '__return_empty_array', 100 );

Expand Down
152 changes: 152 additions & 0 deletions tests/phpunit/tests/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -3129,6 +3129,158 @@ public function test_media_handle_upload_ignores_page_parent_for_directory_date(
$this->assertSame( $expected, $url );
}

/**
* @ticket 46554
*/
public function test_media_handle_upload_time_can_be_filtered_for_page_parent() {
$iptc_file = DIR_TESTDATA . '/images/test-image-iptc.jpg';

// Make a copy of this file as it gets moved during the file upload.
$tmp_name = wp_tempnam( $iptc_file );

copy( $iptc_file, $tmp_name );

$_FILES['upload'] = array(
'tmp_name' => $tmp_name,
'name' => 'test-image-iptc.jpg',
'type' => 'image/jpeg',
'error' => 0,
'size' => filesize( $iptc_file ),
);

$parent_id = self::factory()->post->create(
array(
'post_date' => '2010-01-01 12:00:00',
'post_type' => 'page',
)
);

$filter_args = null;
$filter = static function ( $time, $post, $file, $overrides, $action ) use ( &$filter_args ) {
$filter_args = array(
'time' => $time,
'post' => $post,
'file' => $file,
'overrides' => $overrides,
'action' => $action,
);

if ( $post instanceof WP_Post && 'page' === $post->post_type ) {
return $post->post_date;
}

return $time;
};

add_filter( 'wp_handle_upload_time', $filter, 10, 5 );

$post_id = media_handle_upload(
'upload',
$parent_id,
array(),
array(
'action' => 'test_iptc_upload',
'test_form' => false,
)
);

remove_filter( 'wp_handle_upload_time', $filter, 10 );
unset( $_FILES['upload'] );

$this->assertNotWPError( $post_id );

$url = wp_get_attachment_url( $post_id );
$uploads_dir = wp_upload_dir( '2010/01' );
$expected = $uploads_dir['url'] . '/test-image-iptc.jpg';

// Clean up.
wp_delete_attachment( $post_id, true );
wp_delete_post( $parent_id, true );

$this->assertSame( $expected, $url );
$this->assertIsString( $filter_args['time'] );
$this->assertSame( 'test-image-iptc.jpg', $filter_args['file']['name'] );
$this->assertFalse( $filter_args['overrides']['test_form'] );
$this->assertSame( 'test_iptc_upload', $filter_args['action'] );
$this->assertInstanceOf( WP_Post::class, $filter_args['post'] );
$this->assertSame( $parent_id, $filter_args['post']->ID );
}

/**
* @ticket 46554
*/
public function test_media_handle_sideload_time_can_be_filtered_for_page_parent() {
$iptc_file = DIR_TESTDATA . '/images/test-image-iptc.jpg';

// Make a copy of this file as it gets moved during the file sideload.
$tmp_name = wp_tempnam( $iptc_file );

copy( $iptc_file, $tmp_name );

$file_array = array(
'tmp_name' => $tmp_name,
'name' => 'test-image-iptc.jpg',
'type' => 'image/jpeg',
'error' => 0,
'size' => filesize( $iptc_file ),
);

$parent_id = self::factory()->post->create(
array(
'post_date' => '2010-01-01 12:00:00',
'post_type' => 'page',
)
);

$filter_args = null;
$filter = static function ( $time, $post, $file, $overrides, $action ) use ( &$filter_args ) {
$filter_args = array(
'time' => $time,
'post' => $post,
'file' => $file,
'overrides' => $overrides,
'action' => $action,
);

if ( $post instanceof WP_Post && 'page' === $post->post_type ) {
return $post->post_date;
}

return $time;
};

add_filter( 'wp_handle_upload_time', $filter, 10, 5 );

$post_id = media_handle_sideload(
$file_array,
$parent_id,
null,
array(
'post_date' => '2011-02-01 12:00:00',
)
);

remove_filter( 'wp_handle_upload_time', $filter, 10 );

$this->assertNotWPError( $post_id );

$url = wp_get_attachment_url( $post_id );
$uploads_dir = wp_upload_dir( '2010/01' );
$expected = $uploads_dir['url'] . '/test-image-iptc.jpg';

// Clean up.
wp_delete_attachment( $post_id, true );
wp_delete_post( $parent_id, true );

$this->assertSame( $expected, $url );
$this->assertSame( '2011-02-01 12:00:00', $filter_args['time'] );
$this->assertSame( 'test-image-iptc.jpg', $filter_args['file']['name'] );
$this->assertFalse( $filter_args['overrides']['test_form'] );
$this->assertSame( 'wp_handle_sideload', $filter_args['action'] );
$this->assertInstanceOf( WP_Post::class, $filter_args['post'] );
$this->assertSame( $parent_id, $filter_args['post']->ID );
}

/**
* @ticket 50367
* @requires function imagejpeg
Expand Down
Loading
Loading