diff --git a/README.md b/README.md index d5d2fd73..5eb5c8b7 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ wp media # Import a local image and set it to be the featured image for a post. $ wp media import ~/Downloads/image.png --post_id=123 --title="A downloaded picture" --featured_image - Success: Imported file '/home/person/Downloads/image.png' as attachment ID 1753 and attached to post 123 as featured image. + Imported file '/home/person/Downloads/image.png' as attachment ID 1753 and attached to post 123 as featured image. + Success: Imported 1 of 1 images. # List all registered image sizes $ wp media image-size @@ -46,6 +47,11 @@ wp media | thumbnail | 150 | 150 | hard | +---------------------------+-------+--------+-------+ + # Fix orientation for specific images. + $ wp media fix-orientation 63 + 1/1 Fixing orientation for "Portrait_6" (ID 63). + Success: Fixed 1 of 1 images. + ### wp media import @@ -53,7 +59,7 @@ wp media Creates attachments from local files or URLs. ~~~ -wp media import ... [--post_id=] [--title=] [--caption=<caption>] [--alt=<alt_text>] [--desc=<description>] [--skip-copy] [--preserve-filetime] [--featured_image] [--porcelain] +wp media import <file>... [--post_id=<post_id>] [--post_name=<post_name>] [--file_name=<name>] [--title=<title>] [--caption=<caption>] [--alt=<alt_text>] [--desc=<description>] [--skip-copy] [--preserve-filetime] [--featured_image] [--porcelain[=<field>]] ~~~ **OPTIONS** @@ -66,6 +72,12 @@ wp media import <file>... [--post_id=<post_id>] [--title=<title>] [--caption=<ca [--post_id=<post_id>] ID of the post to attach the imported files to. + [--post_name=<post_name>] + Name of the post to attach the imported files to. + + [--file_name=<name>] + Attachment name (post_name field). + [--title=<title>] Attachment title (post title field). @@ -87,10 +99,14 @@ wp media import <file>... [--post_id=<post_id>] [--title=<title>] [--caption=<ca Remote files will always use the current time. [--featured_image] - If set, set the imported image as the Featured Image of the post its attached to. + If set, set the imported image as the Featured Image of the post it is attached to. - [--porcelain] - Output just the new attachment ID. + [--porcelain[=<field>]] + Output a single field for each imported image. Defaults to attachment ID when used as flag. + --- + options: + - url + --- **EXAMPLES** @@ -129,7 +145,7 @@ wp media import <file>... [--post_id=<post_id>] [--title=<title>] [--caption=<ca Regenerates thumbnails for one or more attachments. ~~~ -wp media regenerate [<attachment-id>...] [--image_size=<image_size>] [--skip-delete] [--only-missing] [--yes] +wp media regenerate [<attachment-id>...] [--image_size=<image_size>] [--skip-delete] [--only-missing] [--delete-unknown] [--yes] ~~~ **OPTIONS** @@ -146,6 +162,9 @@ wp media regenerate [<attachment-id>...] [--image_size=<image_size>] [--skip-del [--only-missing] Only generate thumbnails for images missing image sizes. + [--delete-unknown] + Only delete thumbnails for old unregistered image sizes. + [--yes] Answer yes to the confirmation message. Confirmation only shows when no IDs passed as arguments. diff --git a/features/media-regenerate.feature b/features/media-regenerate.feature index 6e80d5a0..67e3750d 100644 --- a/features/media-regenerate.feature +++ b/features/media-regenerate.feature @@ -1685,3 +1685,39 @@ Feature: Regenerate WordPress attachments Warning: No editor could be selected. """ And the return code should be 1 + + Scenario: Only delete missing image sizes + Given download: + | path | url | + | {CACHE_DIR}/large-image.jpg | http://wp-cli.org/behat-data/large-image.jpg | + And a wp-content/mu-plugins/media-settings.php file: + """ + <?php + add_action( 'after_setup_theme', function(){ + add_image_size( 'test1', 125, 125, true ); + }); + """ + And I run `wp option update uploads_use_yearmonth_folders 0` + + When I run `wp media import {CACHE_DIR}/large-image.jpg --title="My imported attachment" --porcelain` + Then save STDOUT as {ATTACHMENT_ID} + And the wp-content/uploads/large-image-125x125.jpg file should exist + + Given a wp-content/mu-plugins/media-settings.php file: + """ + <?php + add_action( 'after_setup_theme', function(){ + add_image_size( 'test2', 200, 200, true ); + }); + """ + When I run `wp media regenerate --delete-unknown --yes` + Then STDOUT should contain: + """ + Success: Regenerated 1 of 1 images. + """ + And STDOUT should contain: + """ + Deleted unknown image sizes for "My imported attachment" + """ + And the wp-content/uploads/large-image-125x125.jpg file should not exist + And the wp-content/uploads/large-image-200x200.jpg file should not exist diff --git a/src/Media_Command.php b/src/Media_Command.php index 5f5b680b..8275cb45 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -66,6 +66,9 @@ class Media_Command extends WP_CLI_Command { * [--only-missing] * : Only generate thumbnails for images missing image sizes. * + * [--delete-unknown] + * : Only delete thumbnails for old unregistered image sizes. + * * [--yes] * : Answer yes to the confirmation message. Confirmation only shows when no IDs passed as arguments. * @@ -130,6 +133,11 @@ public function regenerate( $args, $assoc_args = array() ) { $skip_delete = true; } + $delete_unknown = Utils\get_flag_value( $assoc_args, 'delete-unknown' ); + if ( $delete_unknown ) { + $skip_delete = false; + } + $additional_mime_types = array(); if ( Utils\wp_version_compare( '4.7', '>=' ) ) { @@ -165,7 +173,7 @@ public function regenerate( $args, $assoc_args = array() ) { if ( 0 === $number % self::WP_CLEAR_OBJECT_CACHE_INTERVAL ) { Utils\wp_clear_object_cache(); } - $this->process_regeneration( $post_id, $skip_delete, $only_missing, $image_size, $number . '/' . $count, $successes, $errors, $skips ); + $this->process_regeneration( $post_id, $skip_delete, $only_missing, $delete_unknown, $image_size, $number . '/' . $count, $successes, $errors, $skips ); } if ( $image_size ) { @@ -587,7 +595,7 @@ private function make_copy( $path ) { return $filename; } - private function process_regeneration( $id, $skip_delete, $only_missing, $image_size, $progress, &$successes, &$errors, &$skips ) { + private function process_regeneration( $id, $skip_delete, $only_missing, $delete_unknown, $image_size, $progress, &$successes, &$errors, &$skips ) { $title = get_the_title( $id ); if ( '' === $title ) { @@ -615,6 +623,14 @@ private function process_regeneration( $id, $skip_delete, $only_missing, $image_ $original_meta = wp_get_attachment_metadata( $id ); + if ( $delete_unknown ) { + $this->delete_unknown_image_sizes( $id, $fullsizepath ); + + WP_CLI::log( "$progress Deleted unknown image sizes for $att_desc." ); + ++$successes; + return; + } + $needs_regeneration = $this->needs_regeneration( $id, $fullsizepath, $is_pdf, $image_size, $skip_delete, $skip_it ); if ( $skip_it ) { @@ -730,7 +746,7 @@ private function needs_regeneration( $att_id, $fullsizepath, $is_pdf, $image_siz return true; } - // Have sizes - check whether there're new ones or they've changed. Note that an attachment can have no sizes if it's on or below the thumbnail threshold. + // Have sizes - check whether they're new ones or they've changed. Note that an attachment can have no sizes if it's on or below the thumbnail threshold. if ( $image_size ) { if ( empty( $image_sizes[ $image_size ] ) ) { @@ -778,8 +794,19 @@ private function image_sizes_differ( $image_sizes, $meta_sizes ) { return false; } - // Like WP's get_intermediate_image_sizes(), but removes sizes that won't be generated for a particular attachment due to its being on or below their thresholds, - // and returns associative array with size name => width/height entries, resolved to crop values if applicable. + /** + * Returns image sizes for a given attachment. + * + * Like WP's get_intermediate_image_sizes(), but removes sizes that won't be generated for a particular attachment due to it being on or below their thresholds, + * and returns associative array with size name => width/height entries, resolved to crop values if applicable. + * + * @param string $fullsizepath Filepath of the attachment + * @param bool $is_pdf Whether it is a PDF. + * @param array $metadata Attachment metadata. + * @param int $att_id Attachment ID. + * + * @return array|WP_Error Image sizes on success, WP_Error instance otherwise. + */ private function get_intermediate_image_sizes_for_attachment( $fullsizepath, $is_pdf, $metadata, $att_id ) { // Need to get width, height of attachment for image_resize_dimensions(). @@ -1323,4 +1350,51 @@ private function get_image_name( $basename, $slug ) { return $slug . '.' . $extension; } + + /** + * Removes files for unknown/unregistered image sizes. + * + * Similar to {@see self::remove_old_images} but also updates metadata afterwards. + * + * @param int $id Attachment ID. + * @param string $fullsizepath Filepath of the attachment. + * + * @return void + */ + private function delete_unknown_image_sizes( $id, $fullsizepath ) { + $original_meta = wp_get_attachment_metadata( $id ); + + $image_sizes = wp_list_pluck( $this->get_registered_image_sizes(), 'name' ); + + $dir_path = dirname( $fullsizepath ) . '/'; + + $sizes_to_delete = array(); + + if ( isset( $original_meta['sizes'] ) ) { + foreach ( $original_meta['sizes'] as $size_name => $size_meta ) { + if ( 'full' === $size_name ) { + continue; + } + + if ( ! in_array( $size_name, $image_sizes, true ) ) { + $intermediate_path = $dir_path . $size_meta['file']; + if ( $intermediate_path === $fullsizepath ) { + continue; + } + + if ( file_exists( $intermediate_path ) ) { + unlink( $intermediate_path ); + } + + $sizes_to_delete[] = $size_name; + } + } + + foreach ( $sizes_to_delete as $size_name ) { + unset( $original_meta['sizes'][ $size_name ] ); + } + } + + wp_update_attachment_metadata( $id, $original_meta ); + } }