Files
doitinpoland.com/wp-content/plugins/sitepress-multilingual-cms/addons/wpml-page-builders/classes/Integrations/Gutenberg/class-wpml-gutenberg-integration.php
2023-09-12 21:41:04 +02:00

370 lines
9.7 KiB
PHP

<?php
use WPML\FP\Fns;
use WPML\FP\Str;
/**
* Class WPML_Gutenberg_Integration
*/
class WPML_Gutenberg_Integration implements \WPML\PB\Gutenberg\Integration {
const PACKAGE_ID = 'Gutenberg';
const GUTENBERG_OPENING_START = '<!-- wp:';
const GUTENBERG_CLOSING_START = '<!-- /wp:';
const CLASSIC_BLOCK_NAME = 'core/classic-block';
/**
* @var WPML\PB\Gutenberg\StringsInBlock\StringsInBlock
*/
private $strings_in_blocks;
/**
* @var WPML_Gutenberg_Config_Option
*/
private $config_option;
/**
* @var WPML_Gutenberg_Strings_Registration $strings_registration
*/
private $strings_registration;
public function __construct(
WPML\PB\Gutenberg\StringsInBlock\StringsInBlock $strings_in_block,
WPML_Gutenberg_Config_Option $config_option,
WPML_Gutenberg_Strings_Registration $strings_registration
) {
$this->strings_in_blocks = $strings_in_block;
$this->config_option = $config_option;
$this->strings_registration = $strings_registration;
}
public function add_hooks() {
add_filter( 'wpml_page_builder_support_required', array( $this, 'page_builder_support_required' ), 10, 1 );
add_action( 'wpml_page_builder_register_strings', array( $this, 'register_strings' ), 10, 2 );
add_action( 'wpml_page_builder_string_translated', array( $this, 'string_translated' ), 10, 5 );
add_filter( 'wpml_config_array', array( $this, 'wpml_config_filter' ) );
add_filter( 'wpml_pb_should_body_be_translated', array( $this, 'should_body_be_translated_filter' ), PHP_INT_MAX, 3 );
add_filter( 'wpml_get_translatable_types', array( $this, 'remove_package_strings_type_filter' ), 11 );
}
/**
* @param array $plugins
*
* @return array
*/
function page_builder_support_required( $plugins ) {
$plugins[] = self::PACKAGE_ID;
return $plugins;
}
/**
* @param WP_Post $post
* @param array $package_data
*/
function register_strings( WP_Post $post, $package_data ) {
if ( $this->is_gutenberg_post( $post ) && self::PACKAGE_ID === $package_data['kind'] ) {
$this->strings_registration->register_strings( $post, $package_data );
}
}
public function register_strings_from_widget( array $blocks, array $package_data ) {
$this->strings_registration->register_strings_from_widget( $blocks, $package_data );
}
/**
* @param WP_Block_Parser_Block|array $block
*
* @return WP_Block_Parser_Block
*/
public static function sanitize_block( $block ) {
if ( ! $block instanceof WP_Block_Parser_Block ) {
if ( empty( $block['blockName'] ) ) {
$block['blockName'] = self::CLASSIC_BLOCK_NAME;
}
$block = new WP_Block_Parser_Block( $block['blockName'], $block['attrs'], $block['innerBlocks'], $block['innerHTML'], $block['innerContent'] );
}
return $block;
}
/**
* @param string $package_kind
* @param int $translated_post_id
* @param WP_Post $original_post
* @param array $string_translations
* @param string $lang
*/
public function string_translated(
$package_kind,
$translated_post_id,
$original_post,
$string_translations,
$lang
) {
if ( self::PACKAGE_ID === $package_kind ) {
$content = $this->replace_strings_in_blocks( $original_post->post_content, $string_translations, $lang );
wpml_update_escaped_post( [ 'ID' => $translated_post_id, 'post_content' => $content ], $lang );
}
}
/**
* @param string $content
* @param array $string_translations
* @param string $lang
*
* @return string
*/
public function replace_strings_in_blocks( $content, $string_translations, $lang ) {
$blocks = self::parse_blocks( $content );
$blocks = $this->update_block_translations( $blocks, $string_translations, $lang );
return Fns::reduce( Str::concat(), '', Fns::map( [ $this, 'render_block' ], $blocks ) );
}
/**
* @param array $blocks
* @param array $string_translations
* @param string $lang
*
* @return array
*/
private function update_block_translations( $blocks, $string_translations, $lang ) {
foreach ( $blocks as &$block ) {
$block = self::sanitize_block( $block );
$block = $this->strings_in_blocks->update( $block, $string_translations, $lang );
if ( isset( $block->innerBlocks ) ) {
$block->innerBlocks = $this->update_block_translations(
$block->innerBlocks,
$string_translations,
$lang
);
}
}
return $blocks;
}
/**
* @param array|WP_Block_Parser_Block $block
*
* @return string
*/
public static function render_block( $block ) {
$content = '';
$block = self::sanitize_block( $block );
if ( self::CLASSIC_BLOCK_NAME !== $block->blockName ) {
$block_content = self::render_inner_HTML( $block );
$block_type = preg_replace( '/^core\//', '', $block->blockName );
$block_attributes = '';
if ( self::has_non_empty_attributes( $block ) ) {
$block_attributes = ' ' . json_encode( $block->attrs, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
}
$content .= self::GUTENBERG_OPENING_START . $block_type . $block_attributes . ' ';
if ( $block_content ) {
$content .= '-->' . $block_content . self::GUTENBERG_CLOSING_START . $block_type . ' -->';
} else {
$content .= '/-->';
}
} else {
$content = wpautop( $block->innerHTML );
}
return $content;
}
public static function has_non_empty_attributes( WP_Block_Parser_Block $block ) {
return (bool) ( (array) $block->attrs );
}
/**
* @param WP_Block_Parser_Block $block
*
* @return string
*/
private static function render_inner_HTML( $block ) {
if ( isset ( $block->innerBlocks ) && count( $block->innerBlocks ) ) {
if ( isset( $block->innerContent ) ) {
$content = self::render_inner_HTML_with_innerContent( $block );
} else {
$content = self::render_inner_HTML_with_guess_parts( $block );
}
} else {
$content = $block->innerHTML;
}
return $content;
}
/**
* Since Gutenberg 4.2.0 and WP 5.0.0 we have a new
* property WP_Block_Parser_Block::$innerContent which
* provides the sequence of inner elements:
* strings or null if it's an inner block.
*
* @see WP_Block_Parser_Block::$innerContent
*
* @param WP_Block_Parser_Block $block
*
* @return string
*/
private static function render_inner_HTML_with_innerContent( $block ) {
$content = '';
$inner_block_index = 0;
foreach ( $block->innerContent as $inner_content ) {
if ( is_string( $inner_content ) ) {
$content .= $inner_content;
} else {
$content .= self::render_block( $block->innerBlocks[ $inner_block_index ] );
$inner_block_index++;
}
}
return $content;
}
/**
* @param WP_Block_Parser_Block $block
*
* @return string
*/
private static function render_inner_HTML_with_guess_parts( $block ) {
$inner_html_parts = self::guess_inner_HTML_parts( $block );
$content = $inner_html_parts[0];
foreach ( $block->innerBlocks as $inner_block ) {
$content .= self::render_block( $inner_block );
}
$content .= $inner_html_parts[1];
return $content;
}
/**
* The gutenberg parser prior to version 4.2.0 (Gutenberg) and 5.0.0 (WP)
* doesn't handle inner blocks correctly.
* It should really return the HTML before and after the blocks
* We're just guessing what it is here
* The usual innerHTML would be: <div class="xxx"></div>
* The columns block also includes new lines: <div class="xxx">\n\n</div>
* So we try to split at ></ and also include white space and new lines between the tags
*
* @param WP_Block_Parser_Block $block
*
* @return array
*/
private static function guess_inner_HTML_parts( $block ) {
$inner_HTML = $block->innerHTML;
$parts = array( $inner_HTML, '' );
switch( $block->blockName ) {
case 'core/media-text':
$html_to_find = '<div class="wp-block-media-text__content">';
$pos = mb_strpos( $inner_HTML, $html_to_find ) + mb_strlen( $html_to_find );
$parts = array(
mb_substr( $inner_HTML, 0, $pos ),
mb_substr( $inner_HTML, $pos )
);
break;
default:
preg_match( '#>\s*</#', $inner_HTML, $matches );
if ( count( $matches ) === 1 ) {
$parts = explode( $matches[0], $inner_HTML );
if ( count( $parts ) === 2 ) {
$match_mid_point = 1 + ( mb_strlen( $matches[0] ) - 3 ) / 2;
// This is the first ">" char plus half the remaining between the tags
$parts[0] .= mb_substr( $matches[0], 0, $match_mid_point );
$parts[1] = mb_substr( $matches[0], $match_mid_point ) . $parts[1];
}
}
break;
}
return $parts;
}
/**
* @param array $config_data
*
* @return array
*/
public function wpml_config_filter( $config_data ) {
$this->config_option->update_from_config( $config_data );
return $config_data;
}
/**
* @param bool $translate
* @param WP_Post $post
* @param string $context
*
* @return bool
*/
public function should_body_be_translated_filter( $translate, WP_Post $post, $context = '' ) {
if ( 'translate_images_in_post_content' === $context && $this->is_gutenberg_post( $post ) ) {
$translate = true;
}
return $translate;
}
/**
* @param WP_Post $post
*
* @return bool
*/
private function is_gutenberg_post( WP_Post $post ) {
return (bool) preg_match( '/' . self::GUTENBERG_OPENING_START . '/', $post->post_content );
}
public static function parse_blocks( $content ) {
global $wp_version;
if ( version_compare( $wp_version, '5.0-beta1', '>=' ) ) {
return parse_blocks( $content );
} else {
// @phpstan-ignore-next-line
return gutenberg_parse_blocks( $content );
}
}
/**
* Remove Gutenberg (string package) from translation dashboard filters
*
* @param array $types
*
* @return mixed
*/
public function remove_package_strings_type_filter( $types ) {
if ( array_key_exists( 'gutenberg', $types ) ) {
unset( $types['gutenberg'] );
}
return $types;
}
}