Skip to content
32 changes: 32 additions & 0 deletions features/media-import.feature
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,35 @@ Feature: Manage WordPress attachments
"""
Error: Invalid value for <porcelain>: invalid. Expected flag or 'url'.
"""

Scenario: Upload files into a custom directory, relative to ABSPATH, when --destination-dir flag is applied.
Given download:
| path | url |
| {CACHE_DIR}/large-image.jpg | http://wp-cli.org/behat-data/large-image.jpg |
When I run `wp media import --destination-dir="foo" {CACHE_DIR}/large-image.jpg --porcelain=url`

Then STDOUT should not contain:
"""
https://example.com/wp-content/uploads/
"""

And STDOUT should contain:
"""
https://example.com/foo/large-image.jpg
"""

Scenario: Upload files into a custom directory, not relative to ABSPATH, when --destination-dir flag is applied.
Given download:
| path | url |
| {CACHE_DIR}/large-image.jpg | http://wp-cli.org/behat-data/large-image.jpg |
When I run `wp media import --destination-dir="{RUN_DIR}/foo" {CACHE_DIR}/large-image.jpg --porcelain=url`

Then STDOUT should not contain:
"""
https://example.com/wp-content/uploads/
"""

And STDOUT should contain:
"""
/foo/large-image.jpg
"""
64 changes: 57 additions & 7 deletions src/Media_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ class Media_Command extends WP_CLI_Command {
*/
const WP_CLEAR_OBJECT_CACHE_INTERVAL = 500;

/**
* @var string|null
*/
private $destination_dir;

/**
* Regenerates thumbnails for one or more attachments.
*
Expand Down Expand Up @@ -216,7 +221,13 @@ public function regenerate( $args, $assoc_args = array() ) {
*
* [--skip-copy]
* : If set, media files (local only) are imported to the library but not moved on disk.
* File names will not be run through wp_unique_filename() with this set.
* File names will not be run through wp_unique_filename() with this set. When used, files
* will remain at their current location and will not be copied into any destination directory.
*
* [--destination-dir=<destination-dir>]
* : Path to the destination directory for uploaded imported files.
* Can be absolute or relative to ABSPATH. Ignored when used together with --skip-copy, as
* files are not moved on disk in that case.
*
* [--preserve-filetime]
* : Use the file modified time as the post published & modified dates.
Expand Down Expand Up @@ -266,12 +277,13 @@ public function import( $args, $assoc_args = array() ) {
$assoc_args = wp_parse_args(
$assoc_args,
array(
'file_name' => '',
'title' => '',
'caption' => '',
'alt' => '',
'desc' => '',
'post_name' => '',
'file_name' => '',
'title' => '',
'caption' => '',
'alt' => '',
'desc' => '',
'post_name' => '',
'destination-dir' => '',
)
);

Expand Down Expand Up @@ -411,6 +423,13 @@ public function import( $args, $assoc_args = array() ) {
}
wp_update_attachment_metadata( $success, wp_generate_attachment_metadata( $success, $file ) );
} else {

$destdir = Utils\get_flag_value( $assoc_args, 'destination-dir' );
if ( ! empty( $destdir ) ) {
$this->destination_dir = $destdir;
add_filter( 'upload_dir', [ $this, 'filter_upload_dir' ], PHP_INT_MAX );
}

// Deletes the temporary file.
$success = media_handle_sideload( $file_array, $assoc_args['post_id'], $assoc_args['title'], $post_array );
if ( is_wp_error( $success ) ) {
Expand Down Expand Up @@ -468,6 +487,8 @@ public function import( $args, $assoc_args = array() ) {
++$successes;
}

remove_filter( 'upload_dir', [ $this, 'filter_upload_dir' ], PHP_INT_MAX );

// Report the result of the operation
if ( ! Utils\get_flag_value( $assoc_args, 'porcelain' ) ) {
Utils\report_batch_operation_results( $noun, 'import', count( $args ), $successes, $errors );
Expand Down Expand Up @@ -939,6 +960,35 @@ private function remove_image_size_filters( $image_size_filters ) {
}
}

public function filter_upload_dir( $uploads ) {
if ( ! $this->destination_dir ) {
return $uploads;
}

$upload_dir = $this->destination_dir;

if ( 0 !== strpos( $this->destination_dir, ABSPATH ) ) {
// $dir is absolute, $upload_dir is (maybe) relative to ABSPATH.
$dir = path_join( ABSPATH, $this->destination_dir );
} else {
$dir = $this->destination_dir;
// normalize $upload_dir.
$upload_dir = substr( $this->destination_dir, strlen( ABSPATH ) );
}

$siteurl = get_option( 'siteurl' );
$url = trailingslashit( $siteurl ) . $upload_dir;

return [
'path' => $this->destination_dir,
'url' => $url,
'subdir' => '',
'basedir' => $this->destination_dir,
'baseurl' => $url,
'error' => false,
];
}

// Update attachment sizes metadata just for a particular intermediate image size.
private function update_attachment_metadata_for_image_size( $id, $new_metadata, $image_size, $metadata ) {

Expand Down