Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save davidwolfpaw/fbe020cd282c4f6ad7ed805f5ad72a85 to your computer and use it in GitHub Desktop.

Select an option

Save davidwolfpaw/fbe020cd282c4f6ad7ed805f5ad72a85 to your computer and use it in GitHub Desktop.

Revisions

  1. davidwolfpaw created this gist Mar 10, 2025.
    149 changes: 149 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,149 @@
    <?php
    /**
    * Plugin Name: Sync Attachment Meta
    * Description: Sync attachment titles and alt text across multisite sites. Choose a source site to pull titles and alt text from, and have matching attachments updated on all other sites.
    * Version: 1.0.0
    */

    // Confirm that WP-CLI is available.
    if ( defined( 'WP_CLI' ) && WP_CLI ) {

    /**
    * Sync attachment titles and alt text across multisite sites.
    */
    class Sync_Attachment_Meta_Command {

    /**
    * Sync titles and alt text based on post_title or filename match.
    *
    * <source_site_id> The ID of the source site to pull titles/alt text from.
    *
    * Example Command: wp sync-attachment-meta 1
    */
    public function __invoke( $args ) {
    list( $source_site_id ) = $args;

    if ( ! is_multisite() ) {
    WP_CLI::error( 'This script only works on multisite installs.' );
    }

    $sites = get_sites();

    // Switch to the source site and get attachments.
    switch_to_blog( $source_site_id );
    WP_CLI::log( "Switched to source site: {$source_site_id}" );

    // Get all attachments on the source site.
    // Note: This will only get attachments that are in the media library.
    $attachments = get_posts(
    array(
    'post_type' => 'attachment',
    'post_status' => 'inherit',
    'posts_per_page' => -1,
    )
    );

    // Display count of attachments found.
    WP_CLI::log( 'Found ' . count( $attachments ) . ' attachments on source site.' );

    // Collect source site attachment data indexed by both title and filename.
    $attachment_data = array();

    foreach ( $attachments as $attachment ) {
    $alt_text = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true );
    $file_path = get_post_meta( $attachment->ID, '_wp_attached_file', true );
    $file_name = basename( $file_path );

    // Add both title and filename as keys for quick lookup.
    $attachment_data[ $attachment->post_title ] = array(
    'title' => $attachment->post_title,
    'caption' => $attachment->post_excerpt,
    'alt' => $alt_text,
    'file' => $file_name,
    );

    $attachment_data[ $file_name ] = array(
    'title' => $attachment->post_title,
    'caption' => $attachment->post_excerpt,
    'alt' => $alt_text,
    'file' => $file_name,
    );
    }

    restore_current_blog();

    // Loop through all sites and update matching attachments.
    foreach ( $sites as $site ) {
    $site_id = (int) $site->blog_id;

    // Skip the source site.
    if ( $site_id === (int) $source_site_id ) {
    continue;
    }

    // Display site ID being processed.
    WP_CLI::log( "Processing site ID: {$site_id}" );
    switch_to_blog( $site_id );

    $site_attachments = get_posts(
    array(
    'post_type' => 'attachment',
    'post_status' => 'inherit',
    'posts_per_page' => -1,
    )
    );

    $updated_count = 0;

    foreach ( $site_attachments as $site_attachment ) {
    $site_file_path = get_post_meta( $site_attachment->ID, '_wp_attached_file', true );
    $site_file_name = basename( $site_file_path );
    $site_title = $site_attachment->post_title;

    $matched_data = null;

    // Check if there's a match by post_title or file_name.
    if ( isset( $attachment_data[ $site_title ] ) ) {
    $matched_data = $attachment_data[ $site_title ];
    } elseif ( isset( $attachment_data[ $site_file_name ] ) ) {
    $matched_data = $attachment_data[ $site_file_name ];
    }

    if ( $matched_data ) {
    // Update post title and caption.
    wp_update_post(
    array(
    'ID' => $site_attachment->ID,
    'post_title' => $matched_data['title'],
    'post_excerpt' => $matched_data['caption'],
    )
    );

    // Check if alt text already exists on this attachment.
    $existing_alt_text = get_post_meta( $site_attachment->ID, '_wp_attachment_image_alt', true );

    if ( empty( $existing_alt_text ) ) {
    // If there is no alt text, add it.
    update_post_meta( $site_attachment->ID, '_wp_attachment_image_alt', $matched_data['alt'] );

    WP_CLI::log( "Updated attachment ID {$site_attachment->ID} on site {$site_id} (matched by title or filename: {$matched_data['file']}, alt text added)" );
    } else {
    // Alt text already exists, skip updating
    WP_CLI::log( "Skipped alt text for attachment ID {$site_attachment->ID} on site {$site_id} (alt text already present)" );
    }

    ++$updated_count;
    }
    }

    WP_CLI::log( "Site {$site_id} complete. Updated {$updated_count} attachments." );

    restore_current_blog();
    }

    WP_CLI::success( 'Attachment meta sync complete!' );
    }
    }

    WP_CLI::add_command( 'sync-attachment-meta', 'Sync_Attachment_Meta_Command' );
    }