Files
2025-02-24 22:33:42 +01:00

302 lines
8.0 KiB
PHP

<?php
/**
* @package Polylang-Pro
*/
/**
* A Service to collect linked post ids.
*
* @since 3.3
*/
class PLL_Collect_Linked_Posts {
/**
* Stores the plugin options.
*
* @var array
*/
protected $options;
/**
* PLL_Collect_Linked_Posts constructor.
*
* @since 3.3
*
* @param array $options The plugin options.
*/
public function __construct( $options ) {
$this->options = $options;
}
/**
* Gets all the post ids linked to a set of posts.
*
* @since 3.3
* @since 3.4 Parameter changed from int[] to WP_Post[].
* @since 3.5 Renamed from get_linked_post_ids and now returns a WP_Post array.
* Also added a second parameter for the post types to retrieve.
*
* @param WP_Post[] $posts The posts for which searching linked posts.
* @param string[] $post_types Limits the linked posts search to these post types.
* @return WP_Post[] An array of linked post objects.
*/
public function get_linked_posts( array $posts, array $post_types ) {
$linked_ids = array();
foreach ( $posts as $post ) {
$linked_ids = array_merge( $linked_ids, $this->get_post_ids_from_post( $post ) );
}
$linked_ids = array_unique( $linked_ids );
if ( empty( $linked_ids ) ) {
return array();
}
// Query all the linked posts outside the PLL_Export_Bulk_Option::translate() loop to avoid multiple SQL queries with get_post() call.
return get_posts(
array(
'include' => $linked_ids,
'post_type' => $post_types,
'post_status' => 'any',
'orderby' => 'ID',
'order' => 'ASC',
)
);
}
/**
* Gets the post ids from a post, wether it's classic or block edited.
*
* @since 3.3
*
* @param WP_Post $post A given WP_Post object.
* @return int[] An array of post ids.
*/
protected function get_post_ids_from_post( $post ) {
$linked_ids = array();
if ( function_exists( 'has_blocks' ) && has_blocks( $post->post_content ) ) {
$linked_ids = $this->get_post_ids_from_block_content( $post->post_content );
} elseif ( $this->options['media_support'] ) {
$linked_ids = $this->get_medias_from_html_content( $post->post_content );
}
if ( $this->options['media_support'] && has_post_thumbnail( $post->ID ) ) {
$linked_ids[] = get_post_thumbnail_id( $post->ID );
}
$linked_ids = array_filter( $linked_ids ); // Clean up the array.
/**
* Filters the medias linked to a post.
*
* @since 3.3
*
* @param int[] $linked_ids Post ids attached to a post (could be in content or in post metas).
* @param int $post_id The post id the post we get other post from.
*/
$linked_ids = apply_filters( 'pll_collect_post_ids', $linked_ids, $post->ID );
return array_unique( $linked_ids );
}
/**
* Gets the post ids from block type content.
*
* @since 3.3
*
* @param string $post_content The content of the post.
* @return int[] An array of post ids.
*/
protected function get_post_ids_from_block_content( $post_content ) {
$blocks = parse_blocks( $post_content );
return $this->get_post_ids_from_blocks_deep( $blocks );
}
/**
* Gets the post ids from blocks.
*
* @since 3.3
*
* @param array $blocks An array of blocks.
* @return int[] An array of post ids.
*/
protected function get_post_ids_from_blocks_deep( $blocks ) {
$post_ids = array();
foreach ( $blocks as $block ) {
if ( $this->options['media_support'] ) {
$post_ids = array_merge( $post_ids, $this->get_media_ids_from_block( $block ) );
}
$post_ids = array_merge( $post_ids, $this->get_navigation_block_ids( $block ) );
$post_ids = array_merge( $post_ids, $this->get_reusable_block_ids( $block ) );
}
return array_unique( $post_ids );
}
/**
* Gets the media ids from a block.
*
* @since 3.3
*
* @param array $block A representative array of a block.
* @return int[] An array of media ids, empty if none found.
*/
protected function get_media_ids_from_block( $block ) {
$post_ids = array();
switch ( $block['blockName'] ) {
case 'core/audio':
case 'core/cover':
case 'core/file':
case 'core/image':
case 'core/video':
$post_ids[] = $block['attrs']['id'];
break;
case 'core/gallery':
// Backward compatibility with WP < 5.9.
if ( isset( $block['attrs']['ids'] ) && is_array( $block['attrs']['ids'] ) ) {
$post_ids = array_merge( $post_ids, $block['attrs']['ids'] );
}
break;
case 'core/media-text':
$post_ids[] = $block['attrs']['mediaId'];
break;
default:
if ( ! empty( $block['innerHTML'] ) ) {
$post_ids = array_merge( $post_ids, $this->get_medias_from_html_content( $block['innerHTML'] ) );
}
break;
}
if ( ! empty( $block['innerBlocks'] ) ) {
$post_ids = array_merge( $post_ids, $this->get_post_ids_from_blocks_deep( $block['innerBlocks'] ) );
}
return $post_ids;
}
/**
* Gets the media ids from classic type content.
*
* @since 3.3
*
* @param string $post_content The content of the post.
* @return int[]
*/
protected function get_medias_from_html_content( $post_content ) {
return array_merge( $this->get_medias_from_img_tags( $post_content ), $this->get_medias_from_shortcodes( $post_content ) );
}
/**
* Gets media ids from shortcodes.
*
* @since 3.3
*
* @param string $post_content The content of the required post.
* @return int[] The media ids.
*/
protected function get_medias_from_shortcodes( $post_content ) {
$media_ids = array();
if ( preg_match_all( '/' . get_shortcode_regex() . '/s', $post_content, $matches, PREG_SET_ORDER ) ) {
foreach ( $matches as $shortcode ) {
$attributes = shortcode_parse_atts( $shortcode[3] ); // $shortcode[3] returns the shortcode attributes as string.
switch ( $shortcode[2] ) { // $shortcode[2] returns the shortcode name.
case 'caption':
if ( isset( $attributes['id'] ) ) {
preg_match( '/attachment_([0-9]+)/', $attributes['id'], $attr );
$media_ids[] = (int) $attr[1];
}
break;
case 'gallery':
case 'playlist':
if ( isset( $attributes['ids'] ) ) {
$media_ids = array_merge( $media_ids, array_map( 'intval', explode( ',', $attributes['ids'] ) ) );
}
break;
}
}
}
return $media_ids;
}
/**
* Gets media ids from img tags
*
* @since 3.3
*
* @param string $post_content The content of the post to search from.
* @return int[] An array of media ids (empty if no media is found).
*/
protected function get_medias_from_img_tags( $post_content ) {
$media_ids = array();
$textarr = wp_html_split( $post_content );
if ( ! is_array( $textarr ) ) {
return $media_ids;
}
foreach ( $textarr as $text ) {
if ( 0 !== strpos( $text, '<img' ) ) {
continue;
}
$attributes = wp_kses_attr_parse( $text );
if ( ! is_array( $attributes ) ) {
continue;
}
foreach ( $attributes as $attr ) {
if ( 0 === strpos( $attr, 'class' ) && preg_match( '#wp\-image\-([0-9]+)#', $attr, $matches ) ) {
$media_ids[] = (int) $matches[1];
}
}
}
return $media_ids;
}
/**
* Gets id from a reusable block.
* Also recursively get contained reusable block ids.
*
* @since 3.3
*
* @param array $block An array containing block data.
* @return int[] An array of reusable block ids, empty if none found.
*/
protected function get_reusable_block_ids( $block ) {
$ids = array();
if ( 'core/block' !== $block['blockName'] ) {
return $ids;
}
$ids[] = $block['attrs']['ref'];
$linked_block_post = get_post( $block['attrs']['ref'] );
if ( $linked_block_post instanceof WP_Post ) {
$ids = array_merge( $ids, $this->get_post_ids_from_block_content( $linked_block_post->post_content ) );
}
return $ids;
}
/**
* Returns the ID from a navigation block.
*
* @since 3.3
*
* @param array $block An array containing block data.
* @return int[] An array of navigation post IDs, empty if none found.
*/
protected function get_navigation_block_ids( $block ) {
if ( 'core/navigation' !== $block['blockName'] ) {
return array();
}
return array( (int) $block['attrs']['ref'] );
}
}