Skip to content

Instantly share code, notes, and snippets.

@mrfoxtalbot
Last active February 15, 2026 01:27
Show Gist options
  • Select an option

  • Save mrfoxtalbot/d0ba45682e47dd1c828fc3dadcff5f5d to your computer and use it in GitHub Desktop.

Select an option

Save mrfoxtalbot/d0ba45682e47dd1c828fc3dadcff5f5d to your computer and use it in GitHub Desktop.
WP Visual Composer (WP Bakery) Migrate Image shortcodes to regular WP shortcodes
/**
* Plugin Name: VC Single Image Migrator - FULL DEBUG
* Description: Complete debugging + replacement for [vc_single_image image="ID"]
* Version: 5.0
*/
if ( ! defined( 'ABSPATH' ) ) exit;
add_action( 'admin_menu', 'vc_debug_migrator_menu' );
function vc_debug_migrator_menu() {
add_management_page( 'VC Image Migrator', 'VC Image Migrator', 'manage_options', 'vc-image-migrator', 'vc_debug_migrator_page' );
}
function vc_debug_migrator_page() {
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'No access' );
$debug_info = $stats = '';
$dry_run = isset( $_POST['dry_run'] );
if ( isset( $_POST['run'] ) && wp_verify_nonce( $_POST['nonce'], 'vc_debug' ) ) {
$result = vc_debug_migrate( $dry_run );
$stats = '<div class="notice notice-success"><p><strong>RESULTS:</strong><br>
Found: ' . $result['found'] . ' shortcodes<br>
Replaced: ' . $result['replaced'] . ' images<br>
Missing: ' . $result['missing'] . ' images<br>
Posts affected: ' . $result['posts'] . '</p></div>';
$debug_info = '<h3>πŸ“‹ Debug Details (First 10):</h3><table class="wp-list-table widefat striped">';
$debug_info .= '<tr><th>ID</th><th>Shortcode</th><th>URL Found?</th><th>URL</th></tr>';
foreach ( array_slice( $result['details'], 0, 10 ) as $detail ) {
$status = $detail['url'] ? 'βœ… YES' : '❌ NO';
$debug_info .= sprintf(
'<tr><td>%s</td><td><code>%s</code></td><td>%s</td><td>%s</td></tr>',
$detail['id'], esc_html( $detail['shortcode'] ), $status, esc_url( $detail['url'] ?: '-' )
);
}
$debug_info .= '</table>';
if ( count( $result['details'] ) > 10 ) {
$debug_info .= '<p><em>... and ' . ( count( $result['details'] ) - 10 ) . ' more</em></p>';
}
}
?>
<div class="wrap">
<h1>πŸ” VC Single Image Migrator + DEBUG</h1>
<?php echo $stats . $debug_info; ?>
<form method="POST">
<?php wp_nonce_field( 'vc_debug', 'nonce' ); ?>
<p>
<label><input type="checkbox" name="dry_run" <?php checked( $dry_run ); ?>> Dry Run (No DB changes)</label><br>
<input type="submit" name="run" class="button-primary" value="πŸ” SCAN & REPLACE" onclick="return confirm('Backup first!')">
</p>
</form>
<h3>Finds β†’ Replaces:</h3>
<p><code>[vc_single_image image="19862" img_size="full"]</code><br>
↓<br><code>&lt;img src="https://yoursite.com/wp-content/uploads/image.jpg"&gt;</code></p>
</div>
<?php
}
function vc_debug_migrate( $dry_run = true ) {
global $wpdb;
$posts = $wpdb->get_results("SELECT ID, post_content FROM {$wpdb->posts} WHERE post_content LIKE '%vc_single_image%' AND post_status != 'trash'");
$found = $replaced = $missing = $posts_changed = 0;
$details = [];
foreach ( $posts as $post ) {
$content = $post->post_content;
$original = $content;
if ( preg_match_all( '/\[vc_single_image\s+image=["\'](\d+)["\'][^]]*]/', $content, $matches, PREG_SET_ORDER ) ) {
foreach ( $matches as $match ) {
$image_id = $match[1];
$shortcode = $match[0];
$details[] = [
'id' => $image_id,
'shortcode' => $shortcode,
'url' => wp_get_attachment_image_url( $image_id, 'full' )
];
$image_url = $details[ count( $details ) - 1 ]['url'];
if ( $image_url ) {
$img_tag = '<img src="' . esc_url( $image_url ) . '" class="wp-image-' . $image_id . '" alt="' . esc_attr( get_the_title( $image_id ) ) . '">';
$content = str_replace( $shortcode, $img_tag, $content );
$replaced++;
} else {
$missing++;
}
$found++;
}
}
if ( $content !== $original && ! $dry_run ) {
wp_update_post( [ 'ID' => $post->ID, 'post_content' => $content ] );
$posts_changed++;
}
}
return [
'found' => $found,
'replaced' => $replaced,
'missing' => $missing,
'posts' => $posts_changed,
'details' => $details
];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment