Skip to content

Instantly share code, notes, and snippets.

@gbot
Last active March 20, 2023 20:24
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save gbot/0c10f03df28315fbe9ad8968ab110285 to your computer and use it in GitHub Desktop.
Save gbot/0c10f03df28315fbe9ad8968ab110285 to your computer and use it in GitHub Desktop.
Migrate a WordPress database so that existing media items are served from Amazon S3 or CloudFront. Requires the WP Offload S3 Lite plugin.
{
"use_https": "",
"purge_amazonS3_info": ""
}
<?php
/* ==================================================================================================================
SCRIPT: 'wp_offload_media_db_migrate.php'
VERSION: 1.0.3
DESCRIPTION: The purpose of this script is to migrate a WordPress database so that existing media items are served from
Amazon S3 or CloudFront. WP Offload Media Lite works great for new media, but some hacking is required to support media
added *before* you started using it.
**** IMPORTANT NOTES REGARDING THIS SCRIPT ****
- This script is NOT FULLY TESTED!
- This script requires PHP version >= 5.6
- This script has NOT BEEN TESTED ON WINDOWS servers.
- The WP Offload Media Lite plugin must be installed and activated on the site.
- Put this file in your WordPress root directory, or where ever the wp-config.php file is.
- This script does NOT upload any media - you must manually upload the media BEFORE running this script.
- Place additional configuration options in wp_offload_media_db_migrate.json, and place it in the SAME directory as this script.
- *Tested on WP 5.0.2, PHP 7.2.11 AND WP Offload Media Lite 2.0.1*
**** IMPORTANT NOTES REGARDING $purge_amazonS3_info variable ****
- $purge_amazonS3_info is set to FALSE by default, and this is the recommended setting.
- You can override this by defining "purge_amazonS3_info": true in the JSON configuration file.
- Only set to TRUE if you fully understand the implications of doing so!
- When $purge_amazonS3_info is TRUE, ALL existing 'amazonS3_info' meta data will be deleted from the database, prior to
running the migration AND also during a "revert".
Usually, you would only set $purge_amazonS3_info set to TRUE if:
1. There is no amazonS3_info meta data in the database (i.e. no media has been added since activating WP Offload Media Lite) ** OR **
2. The WP Offload Media Lite cache busting option "Object Versioning" is OFF (in which case it's usually safe to delete
existing meta data and allow it to be regenerated by this script, provided you haven't changed the object prefix) ** OR **
3. You intend to perform a "revert" and you want to remove ALL amazonS3_info meta data from the database.
**** IMPORTANT CONSIDERATIONS IF YOU REMOVE MEDIA FROM THE LOCAL SERVER ****
1. If you perform a "revert", with $purge_amazonS3_info set to FALSE:
- No amazonS3_info meta data will be removed from the database.
- To complete the "revert", you will need to disable the WP Offload Media Lite setting "Rewrite File URLs" or
deactivate the plugin.
- If you do this, WORDPRESS MEDIA URL REFERENCES WILL BE BROKEN.
2. If you:
- You perform a "revert" with $purge_amazonS3_info set to TRUE ** OR **
- You disable "Rewrite File URLs" in WP Offload Media Lite settings ** OR **
- You deactivate WP Offload Media Lite.
- If you do any of the above, WORDPRESS MEDIA URL REFERENCES WILL BE BROKEN.
If any of the above apply, you will need to manually copy the affected media back to the local server.
**** INSTRUCTIONS ****
1) TURN OFF CACHING. Re-enable it after everything is done.
2) Install and configure the WP Offload Media Lite plugin
3) Manually upload Media Library contents to the bucket you configured in WP Offload Media Lite (including the object prefix)
4) Run this script
================================================================================================================== */
/*================================================================
= Set up default vars and helper functions =
================================================================*/
$error_occurred = FALSE;
$display_errors = FALSE;
$query_string_append = '';
$script_uri = getCurrentUrl();
// exit tidily...
function __exit() {
echo "\n</body>\n</html>";
exit();
}
/*==========================================
= Output HTML header =
==========================================*/
?><!doctype html>
<html>
<head>
<title>WP Offload Media Lite Database Migrator</title>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300' rel='stylesheet'>
<style>
body {font-family: 'Open Sans';font-weight:300;font-size:16px;margin:2em auto; width:50%;}
.toggle-content {display: none}
.toggle-content.is-visible {display: block}
pre {background-color: #f0f0f0;padding:.5em;border-radius:4px;}
/* buttons styling */
.centered{margin:50px auto;text-align:center}.button::-moz-focus-inner{border:0;padding:0}.button{display:inline-block;*display:inline;zoom:1;padding:6px 20px;margin:0;cursor:pointer;border:1px solid #bbb;overflow:visible;font:bold 13px arial,helvetica,sans-serif;text-decoration:none;white-space:nowrap;color:#555;background-color:#ddd;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,1)),to(rgba(255,255,255,0)));background-image:-webkit-linear-gradient(top,rgba(255,255,255,1),rgba(255,255,255,0));background-image:-moz-linear-gradient(top,rgba(255,255,255,1),rgba(255,255,255,0));background-image:-ms-linear-gradient(top,rgba(255,255,255,1),rgba(255,255,255,0));background-image:-o-linear-gradient(top,rgba(255,255,255,1),rgba(255,255,255,0));background-image:linear-gradient(top,rgba(255,255,255,1),rgba(255,255,255,0));-webkit-transition:background-color .2s ease-out;-moz-transition:background-color .2s ease-out;-ms-transition:background-color .2s ease-out;-o-transition:background-color .2s ease-out;transition:background-color .2s ease-out;background-clip:padding-box;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;-moz-box-shadow:0 1px 0 rgba(0,0,0,.3),0 2px 2px -1px rgba(0,0,0,.5),0 1px 0 rgba(255,255,255,.3) inset;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.3),0 2px 2px -1px rgba(0,0,0,.5),0 1px 0 rgba(255,255,255,.3) inset;box-shadow:0 1px 0 rgba(0,0,0,.3),0 2px 2px -1px rgba(0,0,0,.5),0 1px 0 rgba(255,255,255,.3) inset;text-shadow:0 1px 0 rgba(255,255,255,.9);-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.button:hover{background-color:#eee;color:#555}.button:active{background:#e9e9e9;position:relative;top:1px;text-shadow:none;-moz-box-shadow:0 1px 1px rgba(0,0,0,.3) inset;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.3) inset;box-shadow:0 1px 1px rgba(0,0,0,.3) inset}.button[disabled],.button[disabled]:hover,.button[disabled]:active{border-color:#eaeaea;background:#fafafa;cursor:default;position:static;color:#999;-moz-box-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}.button.large{padding:12px 30px;text-transform:uppercase}.button.large:active{top:2px}.button.green,.button.red,.button.blue{color:#fff;text-shadow:0 1px 0 rgba(0,0,0,.2);background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.3)),to(rgba(255,255,255,0)));background-image:-webkit-linear-gradient(top,rgba(255,255,255,.3),rgba(255,255,255,0));background-image:-moz-linear-gradient(top,rgba(255,255,255,.3),rgba(255,255,255,0));background-image:-ms-linear-gradient(top,rgba(255,255,255,.3),rgba(255,255,255,0));background-image:-o-linear-gradient(top,rgba(255,255,255,.3),rgba(255,255,255,0));background-image:linear-gradient(top,rgba(255,255,255,.3),rgba(255,255,255,0))}.button.green{background-color:#57a957;border-color:#57a957}.button.green:hover{background-color:#62c462}.button.green:active{background:#57a957}.button.red{background-color:#ca3535;border-color:#c43c35}.button.red:hover{background-color:#ee5f5b}.button.red:active{background:#c43c35}.button.blue{background-color:#269CE9;border-color:#269CE9}.button.blue:hover{background-color:#70B9E8}.button.blue:active{background:#269CE9}.green[disabled],.green[disabled]:hover,.green[disabled]:active{border-color:#57A957;background:#57A957;color:#D2FFD2}.red[disabled],.red[disabled]:hover,.red[disabled]:active{border-color:#C43C35;background:#C43C35;color:#FFD3D3}.blue[disabled],.blue[disabled]:hover,.blue[disabled]:active{border-color:#269CE9;background:#269CE9;color:#93D5FF}.button-group,.button-group li{display:inline-block;*display:inline;zoom:1}.button-group{font-size:0;margin:0;padding:0;background:rgba(0,0,0,.1);border-bottom:1px solid rgba(0,0,0,.1);padding:7px;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px}.button-group li{margin-right:-1px}.button-group .button{font-size:13px;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.button-group .button:active{-moz-box-shadow:0 0 1px rgba(0,0,0,.2) inset,5px 0 5px -3px rgba(0,0,0,.2) inset,-5px 0 5px -3px rgba(0,0,0,.2) inset;-webkit-box-shadow:0 0 1px rgba(0,0,0,.2) inset,5px 0 5px -3px rgba(0,0,0,.2) inset,-5px 0 5px -3px rgba(0,0,0,.2) inset;box-shadow:0 0 1px rgba(0,0,0,.2) inset,5px 0 5px -3px rgba(0,0,0,.2) inset,-5px 0 5px -3px rgba(0,0,0,.2) inset}.button-group li:first-child .button{-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.button-group li:first-child .button:active{-moz-box-shadow:0 0 1px rgba(0,0,0,.2) inset,-5px 0 5px -3px rgba(0,0,0,.2) inset;-webkit-box-shadow:0 0 1px rgba(0,0,0,.2) inset,-5px 0 5px -3px rgba(0,0,0,.2) inset;box-shadow:0 0 1px rgba(0,0,0,.2) inset,-5px 0 5px -3px rgba(0,0,0,.2) inset}.button-group li:last-child .button{-moz-border-radius:0 3px 3px 0;-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.button-group li:last-child .button:active{-moz-box-shadow:0 0 1px rgba(0,0,0,.2) inset,5px 0 5px -3px rgba(0,0,0,.2) inset;-webkit-box-shadow:0 0 1px rgba(0,0,0,.2) inset,5px 0 5px -3px rgba(0,0,0,.2) inset;box-shadow:0 0 1px rgba(0,0,0,.2) inset,5px 0 5px -3px rgba(0,0,0,.2) inset}
</style>
<script>
// toggle element visibility
var toggle = function (elem) {
elem.classList.toggle('is-visible');
};
// click event listener
document.addEventListener('click', function (event) {
if (!event.target.classList.contains('toggle')) return;
event.preventDefault();
var content = document.querySelector(event.target.hash);
if (!content) return;
toggle(content);
}, false);
</script>
</head>
<body>
<?php
echo '<h1>WP Offload Media Lite Database Migrator</h1>';
echo '<p><strong>Update existing WordPress Media Library items with meta data for WP Offload Media Lite. This will switch existing Media Library items to being served from AWS (S3 or CloudFront)</strong></p>';
echo '<p>Also rewrites <code>href=</code> and <code>src=</code> in <code>post_content</code>, so media inserted into posts and pages are also served from AWS.</p>';
echo '<p>Place additional configuration options in <code>wp_offload_s3_media_migrate.json</code> in the same directory as this script.</p>';
echo '<p>Requires the <a href="https://wordpress.org/plugins/amazon-s3-and-cloudfront/" target="_blank">WP Offload Media Lite WordPress plugin</a> by <a href="https://deliciousbrains.com" target="_blank">Delicious Brains</a>.</p>';
echo '<p>This is a rewrite of an original script from: <a href="http://blog.tjnevis.com/wp-offload-s3-quick-fix-for-existing-media/" target="_blank">blog.tjnevis.com</a></p>';
echo '<hr>';
/*==========================================================
= Debugging. Enable error reporting? =
==========================================================*/
if (isset($_GET['display_errors']) && $_GET['display_errors'] === "true") {
ini_set('display_errors', 1);
error_reporting(E_ALL);
$display_errors = TRUE;
$query_string_append .= '&display_errors=true';
}
/*=========================================================================
= Test for required files, plugins, PHP version etc =
=========================================================================*/
// Test for required PHP version
$php_required_version ="5.5";
if ( version_compare( PHP_VERSION, $php_required_version, '<' ) ) {
echo "<p><strong style='color:red'>ERROR:</strong> PHP version $php_required_version or higher required.</p>";
$error_occurred = TRUE;
}
// Test for presence of wp-config.php
if ( file_exists( __DIR__ . "/wp-config.php") ) {
require_once 'wp-config.php';
// Test for WP Offload S3 plugin
if ( ! class_exists( 'Amazon_S3_And_CloudFront' ) ) {
echo "<p><strong style='color:red'>ERROR:</strong> WP Offload Media Lite plugin is not active! <a target='_blank' href='http://" . $_SERVER['HTTP_HOST'] ."/wp-admin/plugin-install.php?s=WP+Offload+Media+Lite&tab=search&type=term'>Install it now</a>.</p>";
$error_occurred = TRUE;
}
} else {
echo "<p><strong style='color:red'>ERROR:</strong> <code>wp-config.php</code> file could not be found. This script must be run from the same directory as the <code>wp-config.php</code> file.</p>";
$error_occurred = TRUE;
}
// If error occured, exit
if ( $error_occurred ) {
echo "<p><strong style='color:red'>ERROR:</strong> Script requirements failed. Exiting...</p>";
__exit();
}
/*---------- Set database credentials ----------*/
$db_host = $wpdb->dbhost;
$db_name = $wpdb->dbname;
$db_user = $wpdb->dbuser;
$db_password = $wpdb->dbpassword;
/*=================================================
= Set script parameters =
=================================================*/
$s3_bucket = $as3cf->get_setting('bucket');
$s3_region = ($as3cf->get_setting('region')) ? $as3cf->get_setting('region') : $as3cf->get_bucket_region($as3cf->get_setting('bucket'));
if ($as3cf->get_setting('domain') === "cloudfront" && $as3cf->get_setting('cloudfront') ) {
$aws_url = $as3cf->get_setting('cloudfront');
} else {
$aws_url = 's3-' . $s3_region . '.amazonaws.com/' . $as3cf->get_setting('bucket') ;
}
$wp_folder_prefix = str_replace( ABSPATH, '', wp_upload_dir()['basedir'] ) . '/';
$aws_folder_prefix = $as3cf->get_setting('enable-object-prefix') ? $as3cf->get_setting('object-prefix') : $wp_folder_prefix;
$use_https = TRUE; // can be overwritten in the JSON configuration file
$purge_amazonS3_info = FALSE; // delete ALL existing amazonS3_info meta data
/*=========================================
= Display Debug info =
=========================================*/
echo '<p><a href="#info" class="button toggle">View debug info</a></p>';
echo '<div class="toggle-content" id="info">';
echo '<h3>WordPress Information</h3>';
echo '<pre>';
echo "<strong>Site name:</strong> " . get_bloginfo('name') . "\n";
echo "<strong>Site URL:</strong> " . get_bloginfo('wpurl') . "\n";
echo "<strong>Version:</strong> " . get_bloginfo('version') . "\n";
echo '<strong>ABSPATH:</strong> ' . ABSPATH . "\n";
echo '<strong>WP Content Directory (WP_CONTENT_DIR):</strong> ' . WP_CONTENT_DIR . "\n";
echo '<strong>WP Plugin Directory (WP_PLUGIN_DIR):</strong> ' . WP_PLUGIN_DIR . "\n";
echo '<strong>WP Content URL (content_url()):</strong> ' . content_url() . "\n";
echo '<strong>WP Plugin URL (plugins_url()):</strong> ' . plugins_url() . "\n";
echo '<strong>WP Uploads (basedir):</strong> ' . wp_upload_dir()['basedir'] . "\n";
echo '<strong>WP Uploads: (baseurl)</strong> ' . wp_upload_dir()['baseurl'] . "\n";
echo '<strong>WP Uploads: (url)</strong> ' . wp_upload_dir()['url'] . "\n";
echo "<strong>WP table_prefix:</strong> $table_prefix" . "\n";
echo '<strong>WP Uploads Use Year / Month Folders:</strong> ' . ((get_option( 'uploads_use_yearmonth_folders' )) ? 'Enabled' : 'Disabled');
echo '</pre>';
echo '<h3>Environment Information</h3>';
echo '<pre>';
echo "<strong>PHP_OS:</strong> " . PHP_OS . "\n";
echo "<strong>SERVER_SOFTWARE:</strong> " . $_SERVER['SERVER_SOFTWARE'] . "\n";
echo "<strong>PHP_DEBUG :</strong> " . PHP_DEBUG . "\n";
echo "<strong>PHP_VERSION:</strong> " . PHP_VERSION . "\n";
echo "<strong>SERVER_NAME:</strong> " . $_SERVER['SERVER_NAME'] . "\n";
echo "<strong>HTTP_HOST:</strong> " . $_SERVER['HTTP_HOST'] . "\n";
echo "<strong>SCRIPT_URI:</strong> " . $script_uri . "\n";
echo "<strong>PHP_SELF:</strong> " . $_SERVER['PHP_SELF'] . "\n";
echo "<strong>SCRIPT_NAME:</strong> " .$_SERVER['SCRIPT_NAME'] . "\n";
echo "<strong>__FILE__:</strong> " . __FILE__ . "\n";
echo "<strong>__DIR__:</strong> " . __DIR__ . "\n";
echo '</pre>';
echo '</div>';
/*==========================================================
= Display WP Offload Media Lite Settings =
==========================================================*/
echo '<p><a href="#wp-offload-s3-settings" class="button toggle">View WP Offload S3 Lite Settings</a></p>';
echo '<div class="toggle-content" id="wp-offload-s3-settings">';
echo '<h3>WP Offload S3 Lite - Settings</h3>';
echo '<pre>';
echo "<strong>S3 Bucket:</strong> " . $as3cf->get_setting('bucket') . "\n";
echo "<strong>S3 Region:</strong> " . $as3cf->get_provider()->get_regions()[$s3_region] . " (" . $s3_region .")\n";
echo "<strong>Copy Files to S3:</strong> " . ( $as3cf->get_setting('copy-to-s3') ? "YES" : "NO" ) . "\n";
echo "<strong>Serve from S3:</strong> " . ( $as3cf->get_setting('serve-from-s3') ? "YES" : "NO" ) . "\n";
echo "<strong>CloudFront / Custom Domain (ON/OFF):</strong> " . $as3cf->get_setting('domain') . "\n";
echo " - <strong>CloudFront / Custom URL:</strong> " . $as3cf->get_setting('cloudfront') . "\n";
echo " - <strong>Virtual Host:</strong> " . $as3cf->get_setting('virtual-host') . "\n";
echo "<strong>Enable Object Prefix:</strong> " . ( $as3cf->get_setting('enable-object-prefix') ? "YES" : "NO" ) . "\n";
echo " - <strong>Object Prefix:</strong> " . $as3cf->get_setting('object-prefix') . "\n";
echo "<strong>Use Year / Month Folders:</strong> " . ( $as3cf->get_setting('use-yearmonth-folders') ? "YES" : "NO" ) . "\n";
echo "<strong>Force HTTPS:</strong> " . ( $as3cf->get_setting('force-https') ? "YES" : "NO" ) . "\n";
echo " - <strong>SSL:</strong> " . $as3cf->get_setting('ssl') . "\n";
echo "<strong>Remove Files From Server:</strong> " . ( $as3cf->get_setting('remove-local-file') ? "YES" : "NO" ) . "\n";
echo "<strong>Object Versioning:</strong> " . ( $as3cf->get_setting('object-versioning') ? "YES" : "NO" ) . "\n";
echo '</pre>';
echo '</div>';
/*====================================================================
= Get additional settings from the config file =
====================================================================*/
// Any variables can be over written by including them in the JSON configuration file
// Check for config json file
$config_file = pathinfo(__FILE__, PATHINFO_FILENAME) . '.json';
if ( file_exists( $config_file ) ) {
$script_config = json_decode( file_get_contents( $config_file ), true );
echo '<p><a href="#script-config" class="button toggle">View the configuration file contents</a></p>';
// assign script_config values to variables
foreach ($script_config as $var => $value) {
$$var = $value;
}
echo '<pre class="toggle-content" id="script-config">';
echo file_get_contents($config_file);
echo '</pre>';
// convert JSON values to booleans and apply default if JSON value === ""
$use_https = $use_https === "" ? TRUE : boolval($use_https);
$purge_amazonS3_info = $purge_amazonS3_info === "" ? FALSE : boolval($purge_amazonS3_info);
} else {
echo '<p><strong>NOTE:</strong> Configuration file not found. All settings will be taken from WP Offload S3 Lite.</p>';
}
/*===================================================
= Check for required settings =
===================================================*/
if ( ! $s3_bucket || ! $s3_region ) {
echo "<p><strong style=color:red>WARNING:</strong> WP Offload S3 Lite setup appears to be incomplete. Either <a target='_blank' href='" . get_bloginfo('wpurl') ."/wp-admin/options-general.php?page=amazon-s3-and-cloudfront'>complete the set up</a> or define values in the JSON configuration file. Minimum required values are <code>aws_url</code>, <code>s3_bucket</code> and <code>s3_region</code>.";
__exit();
}
// ensure folder prefixes have trailing slash
$aws_folder_prefix = rtrim($aws_folder_prefix, '/') . '/';
// set the protocol
$protocol = (boolval($use_https)) ? 'https://' : 'http://';
/*===============================================
= Display Script Settings =
===============================================*/
echo '<h3>Script Configuration</h3>';
echo '<pre>';
echo "<strong>Search URL:</strong> " . get_site_url($wpdb->blogid) . '/' . $wp_folder_prefix . "\n";
echo "<strong>Rewrite URL:</strong> $protocol$aws_url/$aws_folder_prefix" . "\n";
echo "<strong>S3 Bucket:</strong> $s3_bucket" . "\n";
echo "<strong>S3 Region:</strong> " . $as3cf->get_provider()->get_regions()[$s3_region] . " (" . $s3_region . ")\n";
echo "<strong>WP folder prefix:</strong> $wp_folder_prefix" . "\n";
echo "<strong>AWS folder prefix:</strong> $aws_folder_prefix" . "\n";
echo "<strong>Use HTTPS:</strong> " . ( $use_https ? "TRUE" : "FALSE" ) . "\n";
echo "<strong>Purge amazonS3_info:</strong> " . ( $purge_amazonS3_info ? "TRUE" : "FALSE" ) . "\n";
echo '</pre>';
/*==========================================================================
= Backup the local database (if 'exec' is available) =
==========================================================================*/
// PHP 'exec' is often disabled (particularly on shared servers). In that case, backup DB manually!!!
if ( function_exists('exec') && !in_array('exec', explode(',', ini_get('disable_functions'))) ) {
echo "<p><a href='" . getCurrentUrl() . "?action=backup$query_string_append' class='large red button'>BACKUP DATABASE</a></p>";
if ( isset($_GET['action']) && $_GET['action'] === "backup" ) {
$db_backup_dir = __DIR__;
$db_backup_file_name = 'db_backup_' . date('Y-m-d_h-i-s', time()) . '.sql';
$db_backup_file_path = $db_backup_dir . '/' . $db_backup_file_name;
exec("mysqldump --user=$db_user --password=$db_password --host=$db_host $db_name > $db_backup_file_path");
// check that backup file was created, and display a download link
if (file_exists($db_backup_file_path)) {
echo "<h3 style=\"color:green;font-weight:bold\">Database backed up successfully!</h3><p>File location: <code>$db_backup_file_path</code><br>";
echo "Download: <a href=\"" . get_bloginfo('wpurl') . "/$db_backup_file_name\">$db_backup_file_name</a></p>";
} else {
echo "<p><strong style='color:red'>ERROR:</strong> Database backup failed. Backup file could not be found at <code>$db_backup_file_path</code>.";
__exit();
}
}
} else {
echo '<p><strong style="color:red">WARNING:</strong> PHP <code>exec</code> function is disabled! <strong>Backup the database BEFORE running this script!</strong></p>';
}
/*=======================================
= Display Notices =
=======================================*/
if ( $purge_amazonS3_info === TRUE ) {
echo "<p><strong style=\"color:red\">WARNING:</strong> <code>\$purge_amazonS3_info</code> is <code>TRUE</code>. All existing 'amazons3_info' meta data will be <strong>DELETED</strong> from the database.</p>";
}
/*===============================================================
= Display buttons to run migration / revert =
===============================================================*/
echo "<ul class='button-group'>";
echo "<li><a href='$script_uri?action=migrate$query_string_append' class='large green button'>RUN MIGRATION</a></li>";
echo "<li><a href='$script_uri?action=revert$query_string_append' class='large red button'>REVERT MIGRATION</a></li>";
if ( $display_errors ) {
echo "<li><a href='$script_uri' class='large blue button'>TOGGLE DISPLAY ERRORS (ON)</a></li>";
} else {
echo "<li><a href='$script_uri?display_errors=true' class='large button'>TOGGLE DISPLAY ERRORS (OFF)</a></li>";
}
echo "</ul>";
/*=======================================================================
= Revert changes and delete S3 Info from Database =
=======================================================================*/
// Reverses previous changes made by this script so media is served from the local server
// Check for URL parameter: ?acton=revert
if ( isset($_GET['action']) && $_GET['action'] === "revert" ) {
if ($db_connection = mysqli_connect($db_host, $db_user, $db_password, $db_name)) {
// query to delete existing S3 info
$query_delete_s3_info = "DELETE FROM " . $table_prefix . "postmeta WHERE meta_key = 'amazonS3_info';";
// query to reverse post content 'href'
$query_reverse_post_content_href = updatePostContent(
'href',
$table_prefix . 'posts',
get_site_url($wpdb->blogid) . '/' . $wp_folder_prefix,
"$protocol$aws_url/$aws_folder_prefix",
true
);
// query to reverse post conent 'src'
$query_reverse_post_content_src = updatePostContent(
'src',
$table_prefix . 'posts',
get_site_url($wpdb->blogid) . '/' . $wp_folder_prefix,
"$protocol$aws_url/$aws_folder_prefix",
true
);
// run queries
if ( $purge_amazonS3_info === TRUE ) {
echo '<h3><strong>RUNNING QUERY:</strong> ' . $query_delete_s3_info . '</h3>';
if ($db_connection->query($query_delete_s3_info)) {
echo '<p><strong>DONE:</strong> ' . $db_connection->affected_rows . ' rows affected.</p>';
}
} else {
echo "<p><strong>NOTE:</strong> <code>\$purge_amazonS3_info</code> is <code>$purge_amazonS3_info</code>. Existing 'amazons3_info' meta data will remain in the database.</p>";
}
echo '<h3><strong>RUNNING QUERY:</strong> ' . $query_reverse_post_content_href . '</h3>';
if ($db_connection->query($query_reverse_post_content_href)) {
echo '<p><strong>DONE:</strong> ' . $db_connection->affected_rows . ' rows affected.</p>';
}
echo '<h3><strong>RUNNING QUERY:</strong> ' . $query_reverse_post_content_src . '</h3>';
if ($db_connection->query($query_reverse_post_content_src)) {
echo '<p><strong>DONE:</strong> ' . $db_connection->affected_rows . ' rows affected.</p>';
}
}
echo "<h3>DONE rewriting database 'post_content' URLs back to local server.</h3>";
__exit();
}
/*============================================================
= Migrate DB to serve media from AWS =
============================================================*/
// Check for URL parameter: ?action=migrate
if ( isset($_GET['action']) && $_GET['action'] === "migrate" ) {
// Delete any existing 'amazonS3_info' meta data from the database
if ( $purge_amazonS3_info === TRUE ) {
$wpdb->delete($table_prefix . 'postmeta',
array(
'meta_key' => 'amazonS3_info',
)
);
}
/*=======================================================
= Add meta data for Media Library =
=======================================================*/
// Grab the attachments from the database, we need the meta_value (image name), and the parent post ID
$media_to_update = $wpdb->get_results("SELECT * FROM " . $table_prefix . "postmeta WHERE meta_key = '_wp_attached_file'");
// loop through each media item, adding the amazonS3_info meta data
foreach ($media_to_update as $media_item) {
$media_meta_data = serialize(
array(
'bucket' => $s3_bucket,
'key' => $aws_folder_prefix . $media_item->meta_value,
'region' => $s3_region,
));
// Insert the postmeta record that WP Offload S3 Lite uses
$wpdb->insert($table_prefix . 'postmeta',
array(
'post_id' => $media_item->post_id,
'meta_key' => 'amazonS3_info',
'meta_value' => $media_meta_data,
)
);
}
/*============================================
= Rewrite post content =
============================================*/
// Set up database queries
if ($db_connection = mysqli_connect($db_host, $db_user, $db_password, $db_name)) {
// Query to update post content 'href'
$query_post_content_href = updatePostContent(
'href',
$table_prefix . 'posts',
get_site_url($wpdb->blogid) . '/' . $wp_folder_prefix,
"$protocol$aws_url/$aws_folder_prefix"
);
// Query to update post content 'src'
$query_post_content_src = updatePostContent(
'src',
$table_prefix . 'posts',
get_site_url($wpdb->blogid) . '/' . $wp_folder_prefix,
"$protocol$aws_url/$aws_folder_prefix"
);
echo '<h3><strong>RUNNING QUERY:</strong> ' . $query_post_content_href . '</h3>';
if ($db_connection->query($query_post_content_href)) {
echo '<p><strong>TRUE:</strong> ' . $db_connection->affected_rows . ' rows affected.</p>';
}
echo '<h3><strong>RUNNING QUERY:</strong> ' . $query_post_content_src . '</h3>';
if ($db_connection->query($query_post_content_src)) {
echo '<p><strong>TRUE:</strong> ' . $db_connection->affected_rows . ' rows affected.</p>';
}
}
echo "<h3>DONE rewriting database 'post_content' with AWS URLs.</h3>";
__exit();
}
/*======================================================
= Update database 'post_content' =
======================================================*/
function updatePostContent($type, $table, $local_uri, $aws_uri, $revert = false) {
$from = (!$revert) ? $local_uri : $aws_uri;
$to = (!$revert) ? $aws_uri : $local_uri;
return "UPDATE $table SET post_content = replace(post_content, '$type=\"$from', '$type=\"$to');";
}
function getCurrentUrl() {
$url = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
$url .= '://' . $_SERVER['SERVER_NAME'];
$url .= in_array( $_SERVER['SERVER_PORT'], array('80', '443') ) ? '' : ':' . $_SERVER['SERVER_PORT'];
$url .= $_SERVER['REQUEST_URI'];
return $url;
}
?>
</body>
</html>
@kgoedecke
Copy link

@gbot thanks a lot for sharing this, literally saved me hours of work. I additionally had to run a search and replace afterwards to replace links set hardcoded in the theme settings.

wp search-replace //bla.com/wp-content/uploads/2015 //cdn.bla.com/wp-content/uploads/2015

@gbot
Copy link
Author

gbot commented Sep 27, 2018

@kgoedecke You're welcome, glad it worked OK.

You're quite right, this only changes media meta data and post_content, so yes, WP-CLI (or WP Migrate DB) would be required for any other paths you want to rewrite, but I didn't include that because I didn't want to have any other dependencies. I probably should have mentioned that...

@kgoedecke
Copy link

@gbot I hope my comment will help some people. Again thanks for putting in the effort! Cheers.

@gbot
Copy link
Author

gbot commented Oct 3, 2018

With the recent changes to the plugin, namely now supporting Digital Ocean Spaces (and more to come), this script is broken. (Of course, you could use a <2.0 version of the plugin in the meantime)

I'll try to get around to fixing it sometime soon.

@kgoedecke
Copy link

@gbot I'd also suggest adding a check if as3cf is defined:

global $as3cf;

if (! $as3cf instanceof Amazon_S3_And_CloudFront) {
    echo 'ERROR: $as3cf undefined.';
    return;
}

@gbot
Copy link
Author

gbot commented Oct 25, 2018

@kgoedecke Thanks for the suggestion, though I thought this would suffice:

line:127 if ( ! class_exists( 'Amazon_S3_And_CloudFront' ) ) {

The $as3cf variable and class will have been renamed in the new version. The script needs a rewrite now... on the to-do list.

@idimnida
Copy link

@gbot This time, the plug-in that was limited to AWS was changed to DigitalOcean Spaces. You only need to change the function name below.
get_aws () -> get_provider ()

@jashk
Copy link

jashk commented Dec 30, 2018

Hi!

I make some fixes for WP Offload Media Lite 2.0.1 https://gist.github.com/jashk/d164c481223349c733bcea63385e8dc9

  • fix get_aws () -> get_provider ()
  • fix $as3cf->get_setting('region') empty
  • fix SCRIPT_URI when not set on $_SERVER array

@gbot
Copy link
Author

gbot commented Jan 20, 2019

@jashk Thanks for the updates!

@gbot
Copy link
Author

gbot commented Feb 11, 2019

This script has now been updated with changes from @jashk. Version is 1.0.3. I'm testing it on a new project now.

@princeandrew01
Copy link

Thank you very very very much for this script. I been looking to see how to do it for a few weeks as it was annoying when I moved my site from one host to another and I had to move all my media files.

@nasuke0302
Copy link

@gbot is it possible to run this script more than once?
I would like to test it with a few images on s3 before a upload all of them.

@eduardogoncalves
Copy link

@gbot Thanks!

@dcambria
Copy link

dcambria commented Feb 17, 2021

Is it still working? Nothing happening after clicking run migration.
Thank you.

@gbot
Copy link
Author

gbot commented Feb 17, 2021

Is it still working? Nothing happening after clicking run migration.

You'll need to check your error logs. Sorry, I haven't tested this for sometime.

@dcambria
Copy link

dcambria commented Feb 17, 2021 via email

@mepunit
Copy link

mepunit commented Oct 28, 2021

Hi @gbot,

Thank you for sharing the script.

Does it still work?

Also, is it compatible with WooCommerce?

Regards,
Punit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment