-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a CLI migration command to update attachment file names #63
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
cbfd668
Ignore composer.lock
roborourke bdb43d3
Add rename attachment function and test
roborourke 138369a
Fix functionality, ensure caches are cleared
roborourke 2b6e5e0
Rename CLi command class file
roborourke a087386
Update logic to return early instead of nesting
roborourke 9fec46c
Update migration command to run search-replace against image sizes
roborourke 18cf583
Check attachment ID is for an existing image
roborourke 12ffafa
Do not exit on errors
roborourke ad1e649
Use transactions and support include-columns
roborourke File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
/vendor | ||
composer.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<?php | ||
/** | ||
* CLI Commands for Tachyon. | ||
*/ | ||
|
||
class Tachyon_Command extends WP_CLI_Command { | ||
|
||
/** | ||
* Update attachment file names to work with Tachyon. | ||
* | ||
* Certain file names that end in dimensions such as those produced by | ||
* WordPress eg. example-150x150.jpg can cause problems when uploaded | ||
* as an original image. This prevents Tachyon from accurately and | ||
* performantly rewriting the post content. | ||
* | ||
* @subcommand migrate-files | ||
* @synopsis [--network] [--sites-page=<int>] [--include-columns=<columns>] | ||
*/ | ||
public function migrate_files( $args, $assoc_args ) { | ||
global $wpdb; | ||
|
||
$assoc_args = wp_parse_args( $assoc_args, [ | ||
'network' => false, | ||
'sites-page' => 0, | ||
'include-columns' => 'post_content,post_excerpt,meta_value' | ||
] ); | ||
|
||
$sites = [ get_current_blog_id() ]; | ||
if ( $assoc_args['network'] ) { | ||
$sites = get_sites( [ | ||
'fields' => 'ids', | ||
'offset' => $assoc_args['sites-page'], | ||
] ); | ||
} | ||
|
||
// Get a reference to the search replace command class. | ||
// The class uses the `__invoke()` magic method allowing it to be called like a function. | ||
$search_replace = new Search_Replace_Command; | ||
|
||
foreach ( $sites as $site_id ) { | ||
switch_to_blog( $site_id ); | ||
|
||
if ( $assoc_args['network'] ) { | ||
WP_CLI::log( "Processing site {$site_id}" ); | ||
} | ||
|
||
$attachments = $wpdb->get_col( "SELECT post_id | ||
FROM {$wpdb->postmeta} | ||
WHERE meta_key = '_wp_attached_file' | ||
AND meta_value REGEXP '-[[:digit:]]+x[[:digit:]]+\.(jpe?g|png|gif)$';" ); | ||
|
||
WP_CLI::log( sprintf( 'Renaming %d attachments', count( $attachments ) ) ); | ||
|
||
foreach ( $attachments as $attachment_id ) { | ||
$result = Tachyon::_rename_file( $attachment_id ); | ||
if ( is_wp_error( $result ) ) { | ||
WP_CLI::error( $result, false ); | ||
continue; | ||
} | ||
|
||
WP_CLI::success( sprintf( 'Renamed attachment %d successfully, performing search & replace.', $attachment_id ) ); | ||
|
||
// Add the full size to the array. | ||
$result['old']['sizes']['full'] = [ | ||
'file' => $result['old']['file'], | ||
]; | ||
$result['new']['sizes']['full'] = [ | ||
'file' => $result['new']['file'], | ||
]; | ||
|
||
// Store all update queries into one transaction per image. | ||
$wpdb->query( 'START TRANSACTION;' ); | ||
|
||
// Run search replace against each image size. | ||
foreach ( $result['old']['sizes'] as $size => $size_data ) { | ||
if ( ! isset( $result['new']['sizes'][ $size ] ) ) { | ||
WP_CLI::error( sprintf( 'Size "%s" does not exist for updated attachment %d', $size, $attachment_id ), false ); | ||
continue; | ||
} | ||
|
||
WP_CLI::log( sprintf( 'Making replacements for size "%s" %s -> %s', $size, $size_data['file'], $result['new']['sizes'][ $size ]['file'] ) ); | ||
|
||
// Run search & replace. | ||
$search_replace( | ||
[ | ||
// Old. | ||
$size_data['file'], | ||
// New. | ||
$result['new']['sizes'][ $size ]['file'], | ||
], | ||
// Associative array args / command flags. | ||
[ | ||
'include-columns' => $assoc_args['include-columns'], | ||
'quiet' => true, | ||
] | ||
); | ||
} | ||
|
||
$wpdb->query( 'COMMIT;' ); | ||
} | ||
|
||
restore_current_blog(); | ||
} | ||
|
||
WP_CLI::log( 'Flushing cache...' ); | ||
wp_cache_flush(); | ||
WP_CLI::success( 'Done!' ); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<?php | ||
namespace HM\Tachyon\Tests; | ||
|
||
use ReflectionClass; | ||
use Tachyon; | ||
use WP_UnitTestCase; | ||
|
||
/** | ||
* Ensure the tachyon plugin updates gallery and image links. | ||
* | ||
* @ticket 48 | ||
*/ | ||
class Tests_CLI extends WP_UnitTestCase { | ||
|
||
/** | ||
* @var int[] Attachment IDs | ||
*/ | ||
static $attachment_ids; | ||
|
||
/** | ||
* @var array[] Original array of image sizes. | ||
*/ | ||
static $wp_additional_image_sizes; | ||
|
||
/** | ||
* Set up attachments and posts require for testing. | ||
* | ||
* tachyon-1280x719.jpg: 1280x719 | ||
* Photo by Digital Buggu from Pexels | ||
* @link https://www.pexels.com/photo/0-7-rpm-171195/ | ||
*/ | ||
static public function wpSetUpBeforeClass( $factory ) { | ||
global $_wp_additional_image_sizes; | ||
self::$wp_additional_image_sizes = $_wp_additional_image_sizes; | ||
|
||
// Ensure pre WP 5.3.1 behaviour with image file having dimensions in file name. | ||
add_filter( 'wp_unique_filename', __NAMESPACE__ . '\\Tests_CLI::unique_filename_override' ); | ||
|
||
self::$attachment_ids['tachyon-1280x719'] = $factory->attachment->create_upload_object( | ||
realpath( __DIR__ . '/../data/tachyon-1280x719.jpg') | ||
); | ||
|
||
remove_filter( 'wp_unique_filename', __NAMESPACE__ . '\\Tests_CLI::unique_filename_override' ); | ||
} | ||
|
||
/** | ||
* Runs the routine after all tests have been run. | ||
* | ||
* This deletes the files from the uploads directory | ||
* to account for the test suite returning the posts | ||
* table to the original state. | ||
*/ | ||
public static function wpTearDownAfterClass() { | ||
global $_wp_additional_image_sizes; | ||
$_wp_additional_image_sizes = self::$wp_additional_image_sizes; | ||
|
||
$singleton = Tachyon::instance(); // Get Tachyon instance. | ||
$reflection = new ReflectionClass( $singleton ); | ||
$instance = $reflection->getProperty( 'image_sizes' ); | ||
$instance->setAccessible( true ); // Allow modification of image sizes. | ||
$instance->setValue( null, null ); // Reset image sizes for next tests. | ||
$instance->setAccessible( false ); // clean up. | ||
|
||
$uploads_dir = wp_upload_dir()['basedir']; | ||
|
||
$files = glob( $uploads_dir . '/*' ); | ||
array_walk( $files, function ( $file ) { | ||
if ( is_file( $file ) ) { | ||
unlink($file); | ||
} | ||
} ); | ||
rmdir( $uploads_dir ); | ||
} | ||
|
||
/** | ||
* Prevents WP from fixing the file name during upload. | ||
* | ||
* This occurs if the file name contains dimensions as a suffix. | ||
* This is to help test for backwards compat with WP 5.3.0 and earlier. | ||
* | ||
* @param string $filename | ||
* @return string | ||
*/ | ||
public static function unique_filename_override( $filename ) { | ||
if ( strpos( $filename, 'tachyon-1280x719' ) === false ) { | ||
return $filename; | ||
} | ||
|
||
return str_replace( '-1.jpg', '.jpg', $filename ); | ||
} | ||
|
||
public function test_file_renaming() { | ||
$attachment_id = self::$attachment_ids['tachyon-1280x719']; | ||
|
||
// Get the file path. | ||
$file = get_attached_file( $attachment_id ); | ||
$thumb_file = dirname( $file ) . '/' . basename( $file, '.jpg' ) . '-150x150.jpg'; | ||
|
||
// Confirm original file name. | ||
$this->assertEquals( 'tachyon-1280x719.jpg', basename( $file ) ); | ||
// Confirm original exists. | ||
$this->assertTrue( file_exists( $file ), "File $file exists" ); | ||
// Confirm a thumbnail exists. | ||
$this->assertTrue( file_exists( $thumb_file ), "Thumbnail $thumb_file exists" ); | ||
|
||
// Rename the attachment. | ||
$result = Tachyon::_rename_file( $attachment_id ); | ||
$this->assertTrue( is_array( $result ), 'Attachment renamed successfully' ); | ||
|
||
$new_file = get_attached_file( $attachment_id ); | ||
|
||
// Confirm new file name. | ||
$this->assertEquals( 'tachyon-1.jpg', basename( $new_file ) ); | ||
// Confirm old original has been removed. | ||
$this->assertTrue( file_exists( $new_file ), "File $new_file exists" ); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to be reeeeaaalllyyy slow!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! I'm not sure it's really avoidable though. Would be great if
search-replace
supported arrays of things likestr_replace()
... Given it's a one time migration script it might not be so bad but this seems like the most robust way to do it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm ok, lets go with it for now then!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ryan had some better suggestions to make it faster using transactions so going to try that out today