Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: When transferring all in-content images, ensure any image URLs in the content are updated to the new image #1283

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public function push( $post, $args = array() ) {
*/
if ( apply_filters( 'dt_push_post_media', true, $new_post_id, $post_media, $post_id, $args, $this ) ) {
Utils\set_media( $new_post_id, $post_media, [ 'use_filesystem' => true ] );
};
}

$media_errors = get_transient( 'dt_media_errors_' . $new_post_id );

Expand Down
182 changes: 182 additions & 0 deletions includes/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,8 @@ function set_media( $post_id, $media, $args = [] ) {
$media = ( false !== $featured_key ) ? array( $media[ $featured_key ] ) : array();
}

$image_urls_to_update = [];

foreach ( $media as $media_item ) {

$args['source_file'] = $media_item['source_file'];
Expand Down Expand Up @@ -782,6 +784,11 @@ function set_media( $post_id, $media, $args = [] ) {
set_meta( $image_id, $media_item['meta'] );
}

// Save the images that we need to try updating in the content.
if ( 'featured' !== $settings['media_handling'] ) { // TODO: do we want a new setting for this?
$image_urls_to_update[ $image_id ] = $media_item;
}

// Transfer post properties
wp_update_post(
[
Expand All @@ -793,6 +800,11 @@ function set_media( $post_id, $media, $args = [] ) {
);
}

// Update image URLs in content if needed.
if ( ! empty( $image_urls_to_update ) ) {
update_content_image_urls( (int) $post_id, $image_urls_to_update );
}

if ( ! $found_featured_image ) {
delete_post_meta( $post_id, '_thumbnail_id' );
}
Expand Down Expand Up @@ -1031,6 +1043,176 @@ function process_media( $url, $post_id, $args = [] ) {
return (int) $result;
}

/**
* Find and update an image tag.
*
* @param string $content The post content.
* @param array $media_item The old media item details.
* @param int $image_id The new image ID.
* @return string
*/
function update_image_tag( string $content, array $media_item, int $image_id ) {
$processor = new \WP_HTML_Tag_Processor( $content );

while ( $processor->next_tag( 'img' ) ) {
$classes = explode( ' ', $processor->get_attribute( 'class' ) ?? ' ' );

// Only process the image that matches the old ID.
if (
! is_array( $classes ) ||
! in_array( 'wp-image-' . $media_item['id'], $classes, true )
) {
continue;
}

// Try to determine the image size from the size class WordPress adds.
$image_size = 'full';
$classes = explode( ' ', $processor->get_attribute( 'class' ) ?? [] );
$size_classes = array_filter(
$classes,
function ( $image_class ) {
return false !== strpos( $image_class, 'size-' );
}
);

if ( ! empty( $size_classes ) ) {
// If an image happens to have multiple size classes, just use the first.
$size_class = reset( $size_classes );
$image_size = str_replace( 'size-', '', $size_class );
}

$src = wp_get_attachment_image_url( $image_id, $image_size );

// If the image size can't be found, try to get the full size.
if ( ! $src ) {
$src = wp_get_attachment_image_url( $image_id, 'full' );

// If we still don't have an image, skip this block.
if ( ! $src ) {
continue;
}
}

$processor->set_attribute( 'src', $src );
$processor->add_class( 'wp-image-' . $image_id );
$processor->remove_class( 'wp-image-' . $media_item['id'] );
$processor->remove_attribute( 'srcset' );
$processor->remove_attribute( 'sizes' );
}

return $processor->get_updated_html();
}

/**
* Find and update an image block.
*
* @param array $blocks All blocks in a post.
* @param array $media_item The old media item details.
* @param int $image_id The new image ID.
* @return array
*/
function update_image_block( array $blocks, array $media_item, int $image_id ) {
// Find and update all image blocks that match the old image ID.
foreach ( $blocks as $key => $block ) {
// Recurse into inner blocks.
if ( ! empty( $block['innerBlocks'] ) ) {
$blocks[ $key ]['innerBlocks'] = update_image_block( $block['innerBlocks'], $media_item, $image_id );
}

// If the block is an image block and the ID matches, update the ID and URL.
if ( 'core/image' === $block['blockName'] && $media_item['id'] === $block['attrs']['id'] ) {
$image_size = $block['attrs']['sizeSlug'] ?? 'full';

$blocks[ $key ]['attrs']['id'] = $image_id;

$processor = new \WP_HTML_Tag_Processor( $blocks[ $key ]['innerHTML'] );

// Use the HTML API to update the image src and class.
if ( $processor->next_tag( 'img' ) ) {
$src = wp_get_attachment_image_url( $image_id, $image_size );

// If the image size can't be found, try to get the full size.
if ( ! $src ) {
$src = wp_get_attachment_image_url( $image_id, 'full' );

// If we still don't have an image, skip this block.
if ( ! $src ) {
continue;
}
}

$processor->set_attribute( 'src', $src );
$processor->add_class( 'wp-image-' . $image_id );
$processor->remove_class( 'wp-image-' . $media_item['id'] );

$blocks[ $key ]['innerHTML'] = $processor->get_updated_html();
$blocks[ $key ]['innerContent'][0] = $processor->get_updated_html();
Comment on lines +1131 to +1149
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love feedback on if there's a better approach here. I was hoping I could just update the block attributes and then re-render the block to have it rebuild the saved block markup but nothing I tried actually worked, so I ended up with this approach of basically doing a search/replace from the old URL to the new URL

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried using render_block and serialize_block to avoid directly assigning to innerHTML, but that broke the image block due to inconsistency between the content returned by save() and edit().

}
}
}

return $blocks;
}

/**
* Update all old image URLs with the new ones.
*
* @param int $post_id The post ID.
* @param array $images The old image details.
*/
function update_content_image_urls( int $post_id, array $images ) {
$dt_post = new DistributorPost( $post_id );

if ( ! $dt_post ) {
return;
}

/**
* Filter whether image URLS should be updated in the content.
*
* @since x.x.x
* @hook dt_update_content_image_urls
*
* @param {bool} true Whether image URLs should be updated. Default `true`.
* @param {int} $post_id The post ID.
* @param {array} $images The old image details.
*
* @return {bool} Whether image URLs should be updated.
*/
if ( ! apply_filters( 'dt_update_content_image_urls', true, $post_id, $images ) ) {
return;
}

$content = $dt_post->post->post_content;
$has_blocks = $dt_post->has_blocks();

foreach ( $images as $image_id => $media_item ) {
// Process block and classic editor content differently.
if ( $has_blocks ) {
$blocks = parse_blocks( $content );

// Update the image block attributes.
$updated_blocks = update_image_block( $blocks, $media_item, $image_id );
$content = serialize_blocks( $updated_blocks );
} else {
$content = update_image_tag( $content, $media_item, $image_id );
}
}

// No need to update if the content wasn't modified.
if ( $content === $dt_post->post->post_content ) {
return;
}

// Update the post content.
wp_update_post(
[
'ID' => $post_id,
'post_content' => $content,
]
);
}

/**
* Return whether a post type is compatible with the block editor.
*
Expand Down
Loading