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/2148423203a4241fe5364f93a473a7f0 to your computer and use it in GitHub Desktop.

Select an option

Save mrfoxtalbot/2148423203a4241fe5364f93a473a7f0 to your computer and use it in GitHub Desktop.
WP Visual Composer (WP Bakery) Migrate Gallery & Image Carousel shortcodes to regular WP shortcodes
<?php
/**
* Plugin Name: VC Gallery Migrator - NO FATAL ERROR
* Description: SAFE version - replaces [vc_gallery images="20129,20130,20132"...] β†’ [gallery ids="20129,20130,20132"]
* Version: 5.0 - STABLE
*/
if ( ! defined( 'ABSPATH' ) ) exit;
add_action( 'admin_menu', 'vc_gallery_safe_menu' );
function vc_gallery_safe_menu() {
add_management_page( 'VC Gallery Migrator', 'VC Gallery Migrator', 'manage_options', 'vc-gallery-migrator', 'vc_gallery_safe_page' );
}
function vc_gallery_safe_page() {
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'No access' );
$debug_info = $stats = $message = '';
$dry_run = isset( $_POST['dry_run'] ) ? true : false;
if ( isset( $_POST['run'] ) && wp_verify_nonce( $_POST['nonce'], 'vc_gallery_safe' ) ) {
$result = vc_gallery_safe_migrate( $dry_run );
$status_class = $dry_run ? 'notice-warning' : 'notice-success';
$action = $dry_run ? 'PREVIEWED' : 'βœ… REPLACED';
$stats = sprintf(
'<div class="notice %s"><p><strong>%s:</strong><br>
Found: %d galleries | Replaced: %d | Posts updated: %d</p></div>',
$status_class, $action, $result['found'], $result['replaced'], $result['posts']
);
// Simple safe debug table
$debug_info = '<h3>πŸ“‹ GALLERIES FOUND:</h3>';
foreach ( array_slice( $result['details'], 0, 10 ) as $i => $detail ) {
$debug_info .= sprintf(
'<p><strong>Gallery #%d:</strong> <code>%s</code> β†’ <code>[gallery ids="%s"]</code></p>',
$i + 1,
esc_html( $detail['original'] ),
esc_attr( $detail['gallery'] )
);
}
$message = $dry_run
? '<p><strong>βœ… Preview OK! Uncheck "Dry Run" to replace.</strong></p>'
: '<p><strong>πŸŽ‰ All galleries replaced successfully!</strong></p>';
}
?>
<div class="wrap">
<h1>πŸ–ΌοΈ VC Gallery Migrator - SAFE</h1>
<?php echo $stats . $debug_info . $message; ?>
<form method="POST">
<?php wp_nonce_field( 'vc_gallery_safe', 'nonce' ); ?>
<p>
<label>
<input type="checkbox" name="dry_run" <?php checked( $dry_run ); ?>>
Dry Run (Preview - No changes)
</label><br>
<input type="submit" name="run" class="button-primary"
value="<?php echo $dry_run ? 'πŸ” Preview Galleries' : 'πŸš€ Replace All Galleries'; ?>"
onclick="return confirm('<?php echo $dry_run ? 'Preview?' : 'BACKUP FIRST! This replaces ALL VC galleries.'; ?>')">
</p>
</form>
<h3>πŸ”„ Converts exactly:</h3>
<p><code>[vc_gallery interval="3" images="20129,20130,20132" img_size="medium" onclick="img_link_large"]</code><br>
↓<br><code>[gallery ids="20129,20130,20132"]</code></p>
</div>
<?php
}
function vc_gallery_safe_migrate( $dry_run = true ) {
global $wpdb;
$posts = $wpdb->get_results("SELECT ID, post_content FROM {$wpdb->posts} WHERE post_content LIKE '%vc_gallery%' OR post_content LIKE '%vc_images_carousel%' AND post_status != 'trash'");
$found = $replaced = $posts_changed = 0;
$details = [];
foreach ( $posts as $post ) {
$content = $post->post_content;
$original_content = $content;
// βœ… SAFE REGEX - No complex patterns, no callbacks
if ( preg_match_all( '/\[vc_gallery\s+[^]]*images=["\']([^"\']+)["\'][^\]]*\]/i', $content, $matches, PREG_SET_ORDER ) ) {
foreach ( $matches as $match ) {
$ids_string = $match[1]; // "20129,20130,20132"
$full_shortcode = $match[0];
$ids = array_map( 'intval', explode( ',', $ids_string ) );
$ids = array_filter( $ids ); // Remove empty
$gallery_ids = implode( ',', $ids );
$replacement = '[gallery ids="' . $gallery_ids . '"]';
$content = str_replace( $full_shortcode, $replacement, $content );
$details[] = [
'original' => $full_shortcode,
'gallery' => $gallery_ids
];
$found++;
$replaced++;
}
}
// Also check vc_images_carousel
if ( preg_match_all( '/\[vc_images_carousel\s+[^]]*images=["\']([^"\']+)["\'][^\]]*\]/i', $content, $matches, PREG_SET_ORDER ) ) {
foreach ( $matches as $match ) {
$ids_string = $match[1];
$full_shortcode = $match[0];
$ids = array_map( 'intval', explode( ',', $ids_string ) );
$ids = array_filter( $ids );
$gallery_ids = implode( ',', $ids );
$replacement = '[gallery ids="' . $gallery_ids . '"]';
$content = str_replace( $full_shortcode, $replacement, $content );
$details[] = [
'original' => $full_shortcode,
'gallery' => $gallery_ids
];
$found++;
$replaced++;
}
}
if ( $content !== $original_content && ! $dry_run ) {
wp_update_post( [ 'ID' => $post->ID, 'post_content' => $content ] );
$posts_changed++;
}
}
return [
'found' => $found,
'replaced' => $replaced,
'posts' => $posts_changed,
'details' => $details
];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment