diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index 0428347f7..ee3d5a98f 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -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 ); diff --git a/includes/utils.php b/includes/utils.php index 3579a8d67..1a9ddfa5a 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -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']; @@ -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( [ @@ -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' ); } @@ -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(); + } + } + } + + 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. *