Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save davidwolfpaw/1af6ba483c66a7f1a2ac7be581f4b6d2 to your computer and use it in GitHub Desktop.
Save davidwolfpaw/1af6ba483c66a7f1a2ac7be581f4b6d2 to your computer and use it in GitHub Desktop.
Update Image Alt Text in All Blocks (Multisite + Custom Blocks)
<?php
/**
* Plugin Name: Update Image Alt Text in All Blocks (Multisite + Custom Blocks)
* Description: WP-CLI command to update alt text in all images inside blocks for all sites in a multisite network, including custom blocks.
*/
if ( defined( 'WP_CLI' ) && WP_CLI ) {
class Network_Update_All_Image_Alts_Command {
/**
* Run the image alt text update across all sites in the network.
*
* ## EXAMPLES
*
* wp network-update-all-image-alts
*
* @when after_wp_load
*/
public function __invoke() {
if ( ! is_multisite() ) {
WP_CLI::error( 'This command is for multisite installs only.' );
return;
}
$sites = get_sites( array( 'number' => 0 ) );
WP_CLI::log( 'Starting alt text updates on ' . count( $sites ) . ' sites...' );
foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
$site_name = get_bloginfo( 'name' );
WP_CLI::log( "Processing site: {$site_name} (ID: {$site->blog_id})" );
$updated_count = $this->run_image_alt_update_on_current_site();
WP_CLI::log( "Finished {$site_name}: {$updated_count} posts/pages updated.\n" );
restore_current_blog();
}
WP_CLI::success( 'All sites processed!' );
}
/**
* Runs the image alt text update on the current site.
*
* @return int Number of updated posts.
*/
private function run_image_alt_update_on_current_site() {
$args = array(
'post_type' => array( 'post', 'page' ), // Add CPTs if needed.
'post_status' => 'publish',
'posts_per_page' => -1,
);
$query = new WP_Query( $args );
$updated_count = 0;
foreach ( $query->posts as $post ) {
$original_content = $post->post_content;
$updated_content = $this->update_image_alts_in_all_blocks( $original_content );
if ( $updated_content !== $original_content ) {
wp_update_post(
array(
'ID' => $post->ID,
'post_content' => $updated_content,
)
);
WP_CLI::log( "Updated post ID {$post->ID} ({$post->post_title})" );
++$updated_count;
}
}
return $updated_count;
}
/**
* Parses and updates alt text for images in all blocks within post content.
* Uses serialize_blocks() to preserve block markup.
*
* @param string $content The post content.
* @return string Updated content.
*/
private function update_image_alts_in_all_blocks( $content ) {
$blocks = parse_blocks( $content );
$updated_blocks = array();
foreach ( $blocks as $block ) {
$updated_blocks[] = $this->process_any_block_recursive( $block );
}
// Use serialize_blocks() to keep block markup intact.
return serialize_blocks( $updated_blocks );
}
/**
* Recursively process blocks to update image alt text.
*
* @param array $block
* @return array
*/
private function process_any_block_recursive( $block ) {
// === Generic Core Block Handling ===
if ( ! empty( $block['attrs'] ) && ! empty( $block['attrs']['id'] ) ) {
$attachment_id = $block['attrs']['id'];
$media_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
if ( $media_alt ) {
if ( empty( $block['attrs']['alt'] ) || $block['attrs']['alt'] !== $media_alt ) {
$block['attrs']['alt'] = $media_alt;
}
}
}
if ( ! empty( $block['attrs']['images'] ) && is_array( $block['attrs']['images'] ) ) {
foreach ( $block['attrs']['images'] as &$image ) {
if ( ! empty( $image['id'] ) ) {
$attachment_id = $image['id'];
$media_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
if ( $media_alt ) {
if ( empty( $image['alt'] ) || $image['alt'] !== $media_alt ) {
$image['alt'] = $media_alt;
}
}
}
}
}
// === Custom Kirkwood Block Handling ===
if ( $block['blockName'] === 'kirkwood/story-section-block' && ! empty( $block['attrs']['sections'] ) ) {
foreach ( $block['attrs']['sections'] as $section_key => &$section_data ) {
// Main imageID handling
if ( isset( $section_data['imageID'] ) && is_numeric( $section_data['imageID'] ) ) {
$attachment_id = $section_data['imageID'];
$media_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
if ( $media_alt ) {
if ( empty( $section_data['altText'] ) || $section_data['altText'] !== $media_alt ) {
$section_data['altText'] = $media_alt;
}
}
}
// Optional: Mobile ImageID handling
if ( isset( $section_data['mobileImageID'] ) && is_numeric( $section_data['mobileImageID'] ) ) {
$attachment_id = $section_data['mobileImageID'];
$media_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
if ( $media_alt ) {
if ( empty( $section_data['mobileAltText'] ) || $section_data['mobileAltText'] !== $media_alt ) {
$section_data['mobileAltText'] = $media_alt;
}
}
}
}
}
// === Recursively Process Inner Blocks ===
if ( ! empty( $block['innerBlocks'] ) ) {
foreach ( $block['innerBlocks'] as &$inner_block ) {
$inner_block = $this->process_any_block_recursive( $inner_block );
}
}
return $block;
}
}
// Register the WP-CLI command.
WP_CLI::add_command( 'network-update-all-image-alts', 'Network_Update_All_Image_Alts_Command' );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment