first commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class WPML_PB_API_Hooks_Strategy implements IWPML_PB_Strategy {
|
||||
|
||||
/** @var WPML_PB_Factory $factory */
|
||||
private $factory;
|
||||
private $name;
|
||||
|
||||
public function __construct( $name ) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_Post|stdClass $post
|
||||
*/
|
||||
public function register_strings( $post ) {
|
||||
do_action( 'wpml_page_builder_register_strings', $post, $this->get_package_key( $post->ID ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $post_id
|
||||
* @param string $content
|
||||
* @param WPML\PB\Shortcode\StringCleanUp $stringCleanUp
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function register_strings_in_content( $post_id, $content, WPML\PB\Shortcode\StringCleanUp $stringCleanUp ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function set_factory( $factory ) {
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page_id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_package_key( $page_id ) {
|
||||
return array(
|
||||
'kind' => $this->get_package_kind(),
|
||||
'name' => $page_id,
|
||||
'title' => 'Page Builder Page ' . $page_id,
|
||||
'post_id' => $page_id,
|
||||
);
|
||||
}
|
||||
|
||||
public function get_package_kind() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function get_update_post( $package_data) {
|
||||
return $this->factory->get_update_post( $package_data, $this );
|
||||
}
|
||||
|
||||
public function get_content_updater() {
|
||||
return $this->factory->get_api_hooks_content_updater( $this );
|
||||
}
|
||||
|
||||
public function get_package_strings( $package_data ) {
|
||||
return $this->factory->get_string_translations( $this )->get_package_strings( $package_data );
|
||||
}
|
||||
|
||||
public function remove_string( $string_data ) {
|
||||
$this->factory->get_string_translations( $this )->remove_string( $string_data );
|
||||
}
|
||||
|
||||
public function migrate_location( $post_id, $post_content ) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
class WPML_PB_Update_API_Hooks_In_Content {
|
||||
|
||||
/** @var WPML_PB_API_Hooks_Strategy $strategy */
|
||||
private $strategy;
|
||||
|
||||
public function __construct( WPML_PB_API_Hooks_Strategy $strategy ) {
|
||||
$this->strategy = $strategy;
|
||||
}
|
||||
|
||||
public function update( $translated_post_id, $original_post, $string_translations, $lang ) {
|
||||
do_action(
|
||||
'wpml_page_builder_string_translated',
|
||||
$this->strategy->get_package_kind(),
|
||||
$translated_post_id,
|
||||
$original_post,
|
||||
$string_translations,
|
||||
$lang
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $original_content
|
||||
* @param array $string_translations
|
||||
* @param string $lang
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function update_content( $original_content, $string_translations, $lang ) {
|
||||
return $original_content;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
interface IWPML_PB_Strategy {
|
||||
|
||||
/**
|
||||
* @param \WP_Post|stdClass $post
|
||||
*/
|
||||
public function register_strings( $post );
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
* @param string $content
|
||||
* @param WPML\PB\Shortcode\StringCleanUp $stringCleanUp
|
||||
*
|
||||
* @return bool - true if strings were added.
|
||||
*/
|
||||
public function register_strings_in_content( $post_id, $content, WPML\PB\Shortcode\StringCleanUp $stringCleanUp );
|
||||
|
||||
/**
|
||||
* @param WPML_PB_Factory $factory
|
||||
*
|
||||
*/
|
||||
public function set_factory( $factory );
|
||||
|
||||
public function get_package_key( $page_id );
|
||||
public function get_package_kind();
|
||||
public function get_update_post( $package_data );
|
||||
public function get_content_updater();
|
||||
public function get_package_strings( $package_data );
|
||||
public function remove_string( $string_data );
|
||||
public function migrate_location( $post_id, $post_content );
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\PB\Shortcode;
|
||||
|
||||
use WPML\FP\Fns;
|
||||
|
||||
class StringCleanUp {
|
||||
|
||||
/* @var array */
|
||||
private $existingStrings;
|
||||
|
||||
/* @var \WPML_PB_Shortcode_Strategy */
|
||||
private $shortcodeStrategy;
|
||||
|
||||
/**
|
||||
* StringCleanUp constructor.
|
||||
*
|
||||
* @param int $postId
|
||||
* @param \WPML_PB_Shortcode_Strategy $shortcodeStrategy
|
||||
*/
|
||||
public function __construct( $postId, \WPML_PB_Shortcode_Strategy $shortcodeStrategy ) {
|
||||
$this->shortcodeStrategy = $shortcodeStrategy;
|
||||
$this->existingStrings = $shortcodeStrategy->get_package_strings( $shortcodeStrategy->get_package_key( $postId ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get() {
|
||||
return $this->existingStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function remove( $value ) {
|
||||
unset( $this->existingStrings[ md5( $value ) ] );
|
||||
}
|
||||
|
||||
public function cleanUp() {
|
||||
Fns::each( [ $this->shortcodeStrategy, 'remove_string' ], $this->existingStrings );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
use WPML\Convert\Ids;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\PB\ConvertIds\Helper as ConvertIdsHelper;
|
||||
|
||||
class WPML_PB_Config_Import_Shortcode {
|
||||
|
||||
const PB_SHORTCODE_SETTING = 'pb_shortcode';
|
||||
const PB_MEDIA_SHORTCODE_SETTING = 'wpml_pb_media_shortcode';
|
||||
const PB_IDS_SHORTCODE_SETTING = 'wpml_pb_ids_shortcode';
|
||||
|
||||
const TYPE_POST_IDS = 'post-ids';
|
||||
const TYPE_TAXONOMY_IDS = 'taxonomy-ids';
|
||||
|
||||
/** @var WPML_ST_Settings $st_settings */
|
||||
private $st_settings;
|
||||
|
||||
public function __construct( WPML_ST_Settings $st_settings ) {
|
||||
$this->st_settings = $st_settings;
|
||||
}
|
||||
|
||||
public function add_hooks() {
|
||||
add_filter( 'wpml_config_array', array( $this, 'wpml_config_filter' ) );
|
||||
}
|
||||
|
||||
public function wpml_config_filter( $config_data ) {
|
||||
$this->update_shortcodes_config( $config_data );
|
||||
$this->update_ids_shortcodes_config( $config_data );
|
||||
$this->update_media_shortcodes_config( $config_data );
|
||||
|
||||
return $config_data;
|
||||
}
|
||||
|
||||
/** @param array $config_data */
|
||||
private function update_shortcodes_config( $config_data ) {
|
||||
$old_shortcode_data = $this->get_settings();
|
||||
|
||||
$shortcode_data = array();
|
||||
if ( isset ( $config_data['wpml-config']['shortcodes']['shortcode'] ) ) {
|
||||
foreach ( $config_data['wpml-config']['shortcodes']['shortcode'] as $data ) {
|
||||
$ignore_content = isset( $data['tag']['attr']['ignore-content'] )
|
||||
&& $data['tag']['attr']['ignore-content'];
|
||||
|
||||
$attributes = array();
|
||||
if ( isset( $data['attributes']['attribute'] ) ) {
|
||||
|
||||
$data['attributes']['attribute'] = $this->convert_single_attribute_to_multiple_format( $data['attributes']['attribute'] );
|
||||
|
||||
foreach ( $data['attributes']['attribute'] as $attribute ) {
|
||||
|
||||
if ( ! $this->is_string_attribute( $attribute ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $attribute['value'] ) ) {
|
||||
$attribute_encoding = isset( $attribute['attr']['encoding'] ) ? $attribute['attr']['encoding'] : '';
|
||||
$attribute_type = isset( $attribute['attr']['type'] ) ? $attribute['attr']['type'] : '';
|
||||
$attribute_label = isset( $attribute['attr']['label'] ) ? $attribute['attr']['label'] : '';
|
||||
$attributes[] = [
|
||||
'value' => $attribute['value'],
|
||||
'encoding' => $attribute_encoding,
|
||||
'type' => $attribute_type,
|
||||
'label' => $attribute_label,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! ( $ignore_content && empty( $attributes ) ) ) {
|
||||
$shortcode_data[] = [
|
||||
'tag' => [
|
||||
'value' => $data['tag']['value'],
|
||||
'encoding' => isset( $data['tag']['attr']['encoding'] ) ? $data['tag']['attr']['encoding'] : '',
|
||||
'encoding-condition' => isset( $data['tag']['attr']['encoding-condition'] ) ? $data['tag']['attr']['encoding-condition'] : '',
|
||||
'type' => isset( $data['tag']['attr']['type'] ) ? $data['tag']['attr']['type'] : '',
|
||||
'raw-html' => isset( $data['tag']['attr']['raw-html'] ) ? $data['tag']['attr']['raw-html'] : '',
|
||||
'ignore-content' => $ignore_content,
|
||||
'label' => isset( $data['tag']['attr']['label'] ) ? $data['tag']['attr']['label'] : '',
|
||||
],
|
||||
'attributes' => $attributes,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $shortcode_data != $old_shortcode_data ) {
|
||||
$this->st_settings->update_setting( self::PB_SHORTCODE_SETTING, $shortcode_data, true );
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array $config_data */
|
||||
private function update_media_shortcodes_config( $config_data ) {
|
||||
$old_shortcodes_data = $this->get_media_settings();
|
||||
$shortcodes_data = array();
|
||||
|
||||
if ( isset ( $config_data['wpml-config']['shortcodes']['shortcode'] ) ) {
|
||||
|
||||
foreach ( $config_data['wpml-config']['shortcodes']['shortcode'] as $data ) {
|
||||
$shortcode_data = array();
|
||||
|
||||
if ( isset( $data['attributes']['attribute'] ) ) {
|
||||
$attributes = array();
|
||||
|
||||
$data['attributes']['attribute'] = $this->convert_single_attribute_to_multiple_format( $data['attributes']['attribute'] );
|
||||
|
||||
foreach ( $data['attributes']['attribute'] as $attribute ) {
|
||||
|
||||
if ( ! $this->is_media_attribute( $attribute ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $attribute['value'] ) ) {
|
||||
$attribute_type = isset( $attribute['attr']['type'] ) ? $attribute['attr']['type'] : '';
|
||||
$attributes[ $attribute['value'] ] = array( 'type' => $attribute_type );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $attributes ) {
|
||||
$shortcode_data['attributes'] = $attributes;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $data['tag']['attr']['type'] )
|
||||
&& $data['tag']['attr']['type'] === WPML_Page_Builders_Media_Shortcodes::TYPE_URL
|
||||
) {
|
||||
$shortcode_data['content'] = array( 'type' => WPML_Page_Builders_Media_Shortcodes::TYPE_URL );
|
||||
}
|
||||
|
||||
if ( $shortcode_data ) {
|
||||
$shortcode_data['tag'] = array( 'name' => $data['tag']['value'] );
|
||||
$shortcodes_data[] = $shortcode_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $shortcodes_data != $old_shortcodes_data ) {
|
||||
update_option( self::PB_MEDIA_SHORTCODE_SETTING, $shortcodes_data, true );
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array $config_data */
|
||||
private function update_ids_shortcodes_config( $config_data ) {
|
||||
$old_shortcodes_data = $this->get_id_settings();
|
||||
$shortcodes_data = [];
|
||||
|
||||
if ( isset ( $config_data['wpml-config']['shortcodes']['shortcode'] ) ) {
|
||||
|
||||
foreach ( $config_data['wpml-config']['shortcodes']['shortcode'] as $data ) {
|
||||
$attributes = [];
|
||||
|
||||
if ( isset( $data['attributes']['attribute'] ) ) {
|
||||
$data['attributes']['attribute'] = $this->convert_single_attribute_to_multiple_format( $data['attributes']['attribute'] );
|
||||
|
||||
foreach ( $data['attributes']['attribute'] as $attribute ) {
|
||||
|
||||
if ( ! $this->is_id_attribute( $attribute ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $attribute['value'] ) ) {
|
||||
$attributes[ $attribute['value'] ] = ConvertIdsHelper::selectElementType(
|
||||
Obj::path( [ 'attr','sub-type' ], $attribute ),
|
||||
Obj::path( [ 'attr','type' ], $attribute )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $attributes ) {
|
||||
$tag_name = $data['tag']['value'];
|
||||
$shortcodes_data[ $tag_name ] = $attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $shortcodes_data != $old_shortcodes_data ) {
|
||||
update_option( self::PB_IDS_SHORTCODE_SETTING, $shortcodes_data, true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_string_attribute( array $attribute ) {
|
||||
return ! $this->is_id_attribute( $attribute )
|
||||
&& ! $this->is_media_attribute( $attribute );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_id_attribute( array $attribute ) {
|
||||
return ConvertIdsHelper::isValidType( Obj::path( [ 'attr', 'type' ], $attribute ) );
|
||||
}
|
||||
|
||||
private function is_media_attribute( array $attribute ) {
|
||||
$media_attribute_types = array(
|
||||
WPML_Page_Builders_Media_Shortcodes::TYPE_URL,
|
||||
WPML_Page_Builders_Media_Shortcodes::TYPE_IDS,
|
||||
);
|
||||
|
||||
return isset( $attribute['attr']['type'] )
|
||||
&& in_array( $attribute['attr']['type'], $media_attribute_types, true );
|
||||
}
|
||||
|
||||
private function convert_single_attribute_to_multiple_format( array $attribute ) {
|
||||
if ( ! is_numeric( key( $attribute ) ) ) {
|
||||
$attribute = array( $attribute );
|
||||
}
|
||||
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
public function get_settings() {
|
||||
return $this->st_settings->get_setting( self::PB_SHORTCODE_SETTING );
|
||||
}
|
||||
|
||||
public function get_media_settings() {
|
||||
return get_option( self::PB_MEDIA_SHORTCODE_SETTING, array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_id_settings() {
|
||||
return get_option( self::PB_IDS_SHORTCODE_SETTING, [] );
|
||||
}
|
||||
|
||||
public function has_settings() {
|
||||
$settings = $this->get_settings();
|
||||
|
||||
return ! empty( $settings );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
use WPML\PB\Shortcode\StringCleanUp;
|
||||
|
||||
/**
|
||||
* Class WPML_PB_Register_Shortcodes
|
||||
*/
|
||||
class WPML_PB_Register_Shortcodes {
|
||||
|
||||
private $handle_strings;
|
||||
/** @var WPML_PB_Shortcode_Strategy $shortcode_strategy */
|
||||
private $shortcode_strategy;
|
||||
/** @var WPML_PB_Shortcode_Encoding $encoding */
|
||||
private $encoding;
|
||||
/** @var WPML_PB_Reuse_Translations_By_Strategy|null $reuse_translations */
|
||||
private $reuse_translations;
|
||||
|
||||
/** @var StringCleanUp|null */
|
||||
private $existingStrings;
|
||||
|
||||
/** @var int $location_index */
|
||||
private $location_index;
|
||||
|
||||
/**
|
||||
* @param WPML_PB_String_Registration $handle_strings
|
||||
* @param WPML_PB_Shortcode_Strategy $shortcode_strategy
|
||||
* @param WPML_PB_Shortcode_Encoding $encoding
|
||||
* @param WPML_PB_Reuse_Translations_By_Strategy|null $reuse_translations
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_PB_String_Registration $handle_strings,
|
||||
WPML_PB_Shortcode_Strategy $shortcode_strategy,
|
||||
WPML_PB_Shortcode_Encoding $encoding,
|
||||
WPML_PB_Reuse_Translations_By_Strategy $reuse_translations = null
|
||||
) {
|
||||
$this->handle_strings = $handle_strings;
|
||||
$this->shortcode_strategy = $shortcode_strategy;
|
||||
$this->encoding = $encoding;
|
||||
$this->reuse_translations = $reuse_translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $post_id
|
||||
* @param string $content
|
||||
* @param StringCleanUp $externalStringCleanUp
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function register_shortcode_strings(
|
||||
$post_id,
|
||||
$content,
|
||||
StringCleanUp $externalStringCleanUp = null
|
||||
) {
|
||||
|
||||
$any_registered = false;
|
||||
|
||||
$this->location_index = 1;
|
||||
|
||||
$content = apply_filters( 'wpml_pb_shortcode_content_for_translation', $content, $post_id );
|
||||
$content = WPML_PB_Shortcode_Content_Wrapper::maybeWrap( $content, $this->shortcode_strategy->get_shortcodes() );
|
||||
|
||||
$shortcode_parser = $this->shortcode_strategy->get_shortcode_parser();
|
||||
$shortcodes = $shortcode_parser->get_shortcodes( $content );
|
||||
$this->existingStrings = $externalStringCleanUp ?: new StringCleanUp( $post_id, $this->shortcode_strategy );
|
||||
|
||||
if ( $this->reuse_translations ) {
|
||||
$this->reuse_translations->set_original_strings( $this->existingStrings->get() );
|
||||
}
|
||||
|
||||
foreach ( $shortcodes as $shortcode ) {
|
||||
|
||||
if ( $this->should_handle_content( $shortcode ) ) {
|
||||
$shortcode_content = $shortcode['content'];
|
||||
$encoding = $this->shortcode_strategy->get_shortcode_tag_encoding( $shortcode['tag'] );
|
||||
$encoding_condition = $this->shortcode_strategy->get_shortcode_tag_encoding_condition( $shortcode['tag'] );
|
||||
$type = $this->shortcode_strategy->get_shortcode_tag_type( $shortcode['tag'] );
|
||||
$shortcode_content = $this->encoding->decode( $shortcode_content, $encoding, $encoding_condition );
|
||||
$any_registered = $this->register_string( $post_id, $shortcode_content, $shortcode, 'content', $type ) || $any_registered;
|
||||
}
|
||||
|
||||
$attributes = (array) shortcode_parse_atts( $shortcode['attributes'] );
|
||||
$translatable_attributes = $this->shortcode_strategy->get_shortcode_attributes( $shortcode['tag'] );
|
||||
if ( ! empty( $attributes ) ) {
|
||||
foreach ( $attributes as $attr => $attr_value ) {
|
||||
if ( in_array( $attr, $translatable_attributes, true ) ) {
|
||||
$encoding = $this->shortcode_strategy->get_shortcode_attribute_encoding( $shortcode['tag'], $attr );
|
||||
$type = $this->shortcode_strategy->get_shortcode_attribute_type( $shortcode['tag'], $attr );
|
||||
$attr_value = $this->encoding->decode( $attr_value, $encoding );
|
||||
|
||||
$any_registered = $this->register_string( $post_id, $attr_value, $shortcode, $attr, $type ) || $any_registered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->reuse_translations ) {
|
||||
$this->reuse_translations->find_and_reuse( $post_id, $this->existingStrings->get() );
|
||||
}
|
||||
|
||||
if( ! $externalStringCleanUp ) {
|
||||
$this->existingStrings->cleanUp();
|
||||
$this->mark_post_as_migrate_location_done( $post_id );
|
||||
}
|
||||
|
||||
return $any_registered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $shortcode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function should_handle_content( $shortcode ) {
|
||||
$tag = $shortcode['tag'];
|
||||
|
||||
$handle_content = ! (
|
||||
$this->shortcode_strategy->get_shortcode_ignore_content( $tag )
|
||||
|| in_array(
|
||||
$this->shortcode_strategy->get_shortcode_tag_type( $tag ),
|
||||
array(
|
||||
'media-url',
|
||||
'media-ids',
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Allow page builders to override if the shortcode should be handled as a translatable string.
|
||||
*
|
||||
* @since 4.2
|
||||
* @param bool $handle_content.
|
||||
* @param array $shortcode {
|
||||
*
|
||||
* @type string $tag.
|
||||
* @type string $content.
|
||||
* @type string $attributes.
|
||||
* }
|
||||
*/
|
||||
return apply_filters( 'wpml_pb_should_handle_content', $handle_content, $shortcode );
|
||||
}
|
||||
|
||||
function get_updated_shortcode_string_title( $string_id, $shortcode, $attribute ) {
|
||||
$title = $this->shortcode_strategy->get_shortcode_attribute_label( $shortcode['tag'], $attribute );
|
||||
if ( $title ) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$current_title = $this->get_shortcode_string_title( $string_id );
|
||||
|
||||
$current_title_parts = explode( ':', $current_title );
|
||||
$current_title_parts = array_map( 'trim', $current_title_parts );
|
||||
|
||||
$shortcode_tag = $shortcode['tag'];
|
||||
if ( isset( $current_title_parts[1] ) ) {
|
||||
$shortcode_attributes = explode( ',', $current_title_parts[1] );
|
||||
$shortcode_attributes = array_map( 'trim', $shortcode_attributes );
|
||||
}
|
||||
$shortcode_attributes[] = $attribute;
|
||||
sort( $shortcode_attributes );
|
||||
$shortcode_attributes = array_unique( $shortcode_attributes );
|
||||
|
||||
return $shortcode_tag . ': ' . implode( ', ', $shortcode_attributes );
|
||||
}
|
||||
|
||||
function get_shortcode_string_title( $string_id ) {
|
||||
return $this->handle_strings->get_string_title( $string_id );
|
||||
}
|
||||
|
||||
public function register_string( $post_id, $content, $shortcode, $attribute, $editor_type ) {
|
||||
$string_id = 0;
|
||||
|
||||
if ( is_array( $content ) ) {
|
||||
foreach ( $content as $key => $data ) {
|
||||
if ( $data['translate'] ) {
|
||||
$this->register_string( $post_id, $data['value'], $shortcode, $attribute . ' ' . $key, $editor_type );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( $this->existingStrings ) {
|
||||
$this->existingStrings->remove( $content );
|
||||
}
|
||||
try {
|
||||
$string_id = $this->handle_strings->get_string_id_from_package( $post_id, $content );
|
||||
$string_title = $this->get_updated_shortcode_string_title( $string_id, $shortcode, $attribute );
|
||||
$string_id = $this->handle_strings->register_string( $post_id, $content, $editor_type, $string_title, '', $this->location_index );
|
||||
if ( $string_id ) {
|
||||
$this->location_index ++;
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$string_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $string_id !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
*/
|
||||
private function mark_post_as_migrate_location_done( $post_id ) {
|
||||
update_post_meta( $post_id, WPML_PB_Integration::MIGRATION_DONE_POST_META, true );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
use WPML\FP\Logic;
|
||||
use WPML\FP\Maybe;
|
||||
use WPML\FP\Str;
|
||||
use WPML\LIB\WP\Gutenberg;
|
||||
use function WPML\FP\pipe;
|
||||
|
||||
|
||||
class WPML_PB_Shortcode_Content_Wrapper {
|
||||
|
||||
const WRAPPER_SHORTCODE_NAME = 'wpml_string_wrapper';
|
||||
|
||||
/** @var string $content */
|
||||
private $content;
|
||||
|
||||
/** @var array $valid_shortcodes */
|
||||
private $valid_shortcodes;
|
||||
|
||||
/** @var array $shortcodes */
|
||||
private $shortcodes = array();
|
||||
|
||||
/** @var array $content_array */
|
||||
private $content_array;
|
||||
|
||||
/** @var array $insert_wrapper */
|
||||
private $insert_wrapper = array();
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @param array $valid_shortcodes
|
||||
*/
|
||||
public function __construct( $content, array $valid_shortcodes ) {
|
||||
$this->content = $content;
|
||||
$this->valid_shortcodes = $valid_shortcodes;
|
||||
}
|
||||
|
||||
public function get_wrapped_content() {
|
||||
$this->split_content();
|
||||
$this->parse_shortcodes();
|
||||
$this->analyze_unwrapped_text();
|
||||
$this->insert_wrappers();
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a multibyte safe version of `str_split`
|
||||
*/
|
||||
private function split_content() {
|
||||
$length = mb_strlen( $this->content );
|
||||
|
||||
for ( $i = 0; $i < $length; $i++ ) {
|
||||
$this->content_array[] = mb_substr( $this->content, $i, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
private function parse_shortcodes() {
|
||||
$close_bracket_position = false;
|
||||
$content_length = count( $this->content_array );
|
||||
|
||||
for ( $i = 0; $i < $content_length; $i++ ) {
|
||||
if ( false !== $close_bracket_position && $close_bracket_position >= $i ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( '[' === $this->content_array[ $i ] ) {
|
||||
$close_bracket_position = $this->parse_shortcode( $i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $open_bracket_position
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function parse_shortcode( $open_bracket_position ) {
|
||||
$shortcode_name = $this->get_shortcode_name( $open_bracket_position );
|
||||
$close_bracket_position = $this->get_shortcode_end( $open_bracket_position, $shortcode_name );
|
||||
$is_closing = isset( $this->content_array[ $open_bracket_position + 1 ] )
|
||||
&& '/' === $this->content_array[ $open_bracket_position + 1 ];
|
||||
|
||||
if ( ! in_array( $shortcode_name, $this->valid_shortcodes, true ) ) {
|
||||
return $close_bracket_position;
|
||||
}
|
||||
|
||||
if ( $is_closing ) {
|
||||
$shortcode_index = $this->find_last_opened_shortcode( $shortcode_name );
|
||||
|
||||
if ( null !== $shortcode_index ) {
|
||||
$this->shortcodes[ $shortcode_index ]['end'] = $close_bracket_position;
|
||||
$this->remove_nested_shortcodes_between(
|
||||
$this->shortcodes[ $shortcode_index ]['start'],
|
||||
$close_bracket_position
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->shortcodes[] = array(
|
||||
'name' => $shortcode_name,
|
||||
'start' => $open_bracket_position,
|
||||
'end' => $close_bracket_position,
|
||||
);
|
||||
}
|
||||
|
||||
return $close_bracket_position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
*/
|
||||
private function remove_nested_shortcodes_between( $start, $end ) {
|
||||
foreach ( $this->shortcodes as $key => $shortcode ) {
|
||||
|
||||
if ( $start < $shortcode['start'] && $end > $shortcode['end'] ) {
|
||||
unset( $this->shortcodes[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function analyze_unwrapped_text() {
|
||||
$next_unwrapped_text_start = 0;
|
||||
|
||||
foreach ( $this->shortcodes as $shortcode ) {
|
||||
$unwrapped_text_start = $next_unwrapped_text_start;
|
||||
$unwrapped_text_end = $shortcode['start'] - 1;
|
||||
$next_unwrapped_text_start = $shortcode['end'] + 1;
|
||||
|
||||
if ( $unwrapped_text_start < $unwrapped_text_end ) {
|
||||
$this->set_wrapper_positions( $unwrapped_text_start, $unwrapped_text_end );
|
||||
}
|
||||
}
|
||||
|
||||
$max_content_char_position = mb_strlen( $this->content ) - 1;
|
||||
|
||||
// For unwrapped text closing the content.
|
||||
if ( $next_unwrapped_text_start < $max_content_char_position ) {
|
||||
$this->set_wrapper_positions( $next_unwrapped_text_start, $max_content_char_position );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
*/
|
||||
private function set_wrapper_positions( $start, $end ) {
|
||||
$raw_chunk = mb_substr( $this->content, $start, $end - $start );
|
||||
|
||||
if ( '' === trim( $raw_chunk ) ) {
|
||||
// the chunk is an empty string, we don't need to wrap it.
|
||||
return;
|
||||
}
|
||||
|
||||
$chunk_start = $this->get_wrapper_insert_position( $start, 'open' );
|
||||
$unwrapped_text_end = $this->get_wrapper_insert_position( $end, 'close' );
|
||||
|
||||
$this->insert_wrapper[ $chunk_start ] = '[' . self::WRAPPER_SHORTCODE_NAME . ']';
|
||||
$this->insert_wrapper[ $unwrapped_text_end ] = '[/' . self::WRAPPER_SHORTCODE_NAME . ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
* @param string $type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function get_wrapper_insert_position( $position, $type ) {
|
||||
if ( 'close' === $type ) {
|
||||
$increment = - 1;
|
||||
} else {
|
||||
$increment = 1;
|
||||
}
|
||||
|
||||
while ( isset( $this->content_array[ $position ] )
|
||||
&& in_array( $this->content_array[ $position ], array( "\n", "\r" ), true )
|
||||
) {
|
||||
$position = $position + $increment;
|
||||
}
|
||||
|
||||
if ( 'close' === $type ) {
|
||||
$position++;
|
||||
}
|
||||
|
||||
return $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $open_bracket_position
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_shortcode_name( $open_bracket_position ) {
|
||||
$char_position = $open_bracket_position + 1;
|
||||
$name = '';
|
||||
|
||||
while ( isset( $this->content_array[ $char_position ] )
|
||||
&& ( '' === $name || ! in_array( $this->content_array[ $char_position ], array( ' ', ']' ), true ) )
|
||||
) {
|
||||
if ( '/' !== $this->content_array[ $char_position ] ) {
|
||||
$name .= $this->content_array[ $char_position ];
|
||||
}
|
||||
|
||||
$char_position++;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $open_bracket_position
|
||||
* @param string $shortcode_name
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function get_shortcode_end( $open_bracket_position, $shortcode_name ) {
|
||||
$char_position = $open_bracket_position + mb_strlen( $shortcode_name );
|
||||
$is_in_quotes = false;
|
||||
|
||||
while ( isset( $this->content_array[ $char_position ] )
|
||||
&& ( ']' !== $this->content_array[ $char_position ] || $is_in_quotes )
|
||||
) {
|
||||
if ( in_array( $this->content_array[ $char_position ], array( '"', "'" ), true ) ) {
|
||||
$is_in_quotes = ! $is_in_quotes;
|
||||
}
|
||||
|
||||
$char_position++;
|
||||
}
|
||||
|
||||
return $char_position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $shortcode_name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function find_last_opened_shortcode( $shortcode_name ) {
|
||||
$last_matching_index = null;
|
||||
|
||||
foreach ( $this->shortcodes as $shortcode_index => $shortcode ) {
|
||||
if ( $shortcode['name'] === $shortcode_name ) {
|
||||
$last_matching_index = (int) $shortcode_index;
|
||||
}
|
||||
}
|
||||
|
||||
return $last_matching_index;
|
||||
}
|
||||
|
||||
private function insert_wrappers() {
|
||||
$offset = 0;
|
||||
|
||||
foreach ( $this->insert_wrapper as $wrapper_position => $wrapper ) {
|
||||
$insert_position = $wrapper_position + $offset;
|
||||
$before = mb_substr( $this->content, 0, $insert_position );
|
||||
$after = mb_substr( $this->content, $insert_position );
|
||||
$this->content = $before . $wrapper . $after;
|
||||
$offset = $offset + mb_strlen( $wrapper );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @param array $shortcodes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function maybeWrap( $content, array $shortcodes ) {
|
||||
$containsOneShortcode = pipe( Str::match( '/' . get_shortcode_regex( $shortcodes ) . '/s' ), Logic::isEmpty(), Logic::not() );
|
||||
|
||||
return Maybe::of( $content )
|
||||
->filter( Gutenberg::doesNotHaveBlock() )
|
||||
->filter( $containsOneShortcode )
|
||||
->filter( [ self::class, 'isStrippedContentDifferent' ] )
|
||||
->map( [ self::class, 'wrap' ] )
|
||||
->getOrElse( $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* This will flag some regular text not wrapped in a shortcode.
|
||||
* e.g. "[foo] Some text not wrapped [bar]"
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isStrippedContentDifferent( $content ) {
|
||||
$content_with_stripped_shortcode = preg_replace( '/\[([\S]*)[^\]]*\][\s\S]*\[\/(\1)\]|\[[^\]]*\]/', '', $content );
|
||||
$content_with_stripped_shortcode = trim( $content_with_stripped_shortcode );
|
||||
return ! empty( $content_with_stripped_shortcode ) && trim( $content ) !== $content_with_stripped_shortcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function wrap( $content ) {
|
||||
return '[' . self::WRAPPER_SHORTCODE_NAME . ']' . $content . '[/' . self::WRAPPER_SHORTCODE_NAME . ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function unwrap( $content ) {
|
||||
return str_replace(
|
||||
[
|
||||
'[' . self::WRAPPER_SHORTCODE_NAME . ']',
|
||||
'[/' . self::WRAPPER_SHORTCODE_NAME . ']'
|
||||
],
|
||||
'',
|
||||
$content
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_PB_Register_Shortcodes
|
||||
*/
|
||||
class WPML_PB_Shortcode_Encoding {
|
||||
const ENCODE_TYPES_BASE64 = 'base64';
|
||||
const ENCODE_TYPES_VISUAL_COMPOSER_LINK = 'vc_link';
|
||||
const ENCODE_TYPES_ENFOLD_LINK = 'av_link';
|
||||
|
||||
public function decode( $string, $encoding, $encoding_condition = '' ) {
|
||||
$encoded_string = $string;
|
||||
|
||||
if ( $encoding_condition && ! $this->should_decode( $encoding_condition ) ) {
|
||||
return html_entity_decode( $string );
|
||||
}
|
||||
|
||||
switch ( $encoding ) {
|
||||
case self::ENCODE_TYPES_BASE64:
|
||||
$string = html_entity_decode( rawurldecode( base64_decode( strip_tags( $string ) ) ) );
|
||||
break;
|
||||
|
||||
case self::ENCODE_TYPES_VISUAL_COMPOSER_LINK:
|
||||
$parts = explode( '|', $string );
|
||||
$string = array();
|
||||
foreach ( $parts as $part ) {
|
||||
$data = explode( ':', $part );
|
||||
if ( count( $data ) === 2 ) {
|
||||
if ( in_array( $data[0], array( 'url', 'title' ), true ) ) {
|
||||
$string[ $data[0] ] = array( 'value' => urldecode( $data[1] ), 'translate' => true );
|
||||
} else {
|
||||
$string[ $data[0] ] = array( 'value' => urldecode( $data[1] ), 'translate' => false );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ENCODE_TYPES_ENFOLD_LINK:
|
||||
// Note: We can't handle 'lightbox' mode because we don't know how to re-encode it
|
||||
$link = explode( ',', $string, 2 );
|
||||
if ( 'manually' === $link[0] ) {
|
||||
$string = $link[1];
|
||||
} elseif ( post_type_exists( $link[0] ) ) {
|
||||
$string = get_permalink( $link[1] );
|
||||
} elseif ( taxonomy_exists( $link[0] ) ) {
|
||||
$term_link = get_term_link( get_term( $link[1], $link[0] ) );
|
||||
if ( ! is_wp_error( $term_link ) ) {
|
||||
$string = $term_link;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return apply_filters( 'wpml_pb_shortcode_decode', $string, $encoding, $encoded_string );
|
||||
}
|
||||
|
||||
public function encode( $string, $encoding ) {
|
||||
$decoded_string = $string;
|
||||
switch ( $encoding ) {
|
||||
case self::ENCODE_TYPES_BASE64:
|
||||
$string = base64_encode( $string );
|
||||
break;
|
||||
|
||||
case self::ENCODE_TYPES_VISUAL_COMPOSER_LINK:
|
||||
$output = '';
|
||||
if ( is_array( $string ) ) {
|
||||
foreach ( $string as $key => $value ) {
|
||||
$output .= $key . ':' . rawurlencode( $value ) . '|';
|
||||
}
|
||||
}
|
||||
$string = $output;
|
||||
break;
|
||||
|
||||
case self::ENCODE_TYPES_ENFOLD_LINK:
|
||||
$link = explode( ',', $string, 2 );
|
||||
if ( $link[0] !== 'lightbox' ) {
|
||||
$string = 'manually,' . $string;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return apply_filters( 'wpml_pb_shortcode_encode', $string, $encoding, $decoded_string );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $condition
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function should_decode( $condition ) {
|
||||
preg_match( '/(?P<type>\w+):(?P<field>\w+)=(?P<value>\w+)/', $condition, $matches );
|
||||
|
||||
return 'option' === $matches['type'] && get_option( $matches['field'] ) === $matches['value'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
use \WPML\LIB\WP\Gutenberg;
|
||||
use WPML\FP\Obj;
|
||||
|
||||
class WPML_PB_Shortcode_Strategy implements IWPML_PB_Strategy {
|
||||
|
||||
private $shortcodes = array(
|
||||
WPML_PB_Shortcode_Content_Wrapper::WRAPPER_SHORTCODE_NAME => array(
|
||||
'encoding' => '',
|
||||
'encoding-condition' => '',
|
||||
'type' => '',
|
||||
'ignore-content' => false,
|
||||
'attributes' => array(),
|
||||
),
|
||||
);
|
||||
/** @var WPML_PB_Factory $factory */
|
||||
private $factory;
|
||||
|
||||
/** @var WPML_Page_Builder_Settings $page_builder_settings */
|
||||
private $page_builder_settings;
|
||||
|
||||
public function __construct( WPML_Page_Builder_Settings $page_builder_settings ) {
|
||||
$this->page_builder_settings = $page_builder_settings;
|
||||
}
|
||||
|
||||
public function add_shortcodes( $shortcode_data ) {
|
||||
|
||||
foreach ( $shortcode_data as $shortcode ) {
|
||||
$tag = $shortcode['tag']['value'];
|
||||
$is_raw_html = isset( $shortcode['tag']['raw-html'] ) && $shortcode['tag']['raw-html'];
|
||||
|
||||
if ( $is_raw_html && ! $this->page_builder_settings->is_raw_html_translatable() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! in_array( $tag, $this->shortcodes ) ) {
|
||||
$this->shortcodes[ $tag ] = [
|
||||
'encoding' => $shortcode['tag']['encoding'],
|
||||
'encoding-condition' => isset( $shortcode['tag']['encoding-condition'] ) ? $shortcode['tag']['encoding-condition'] : '',
|
||||
'type' => isset( $shortcode['tag']['type'] ) ? $shortcode['tag']['type'] : '',
|
||||
'ignore-content' => isset( $shortcode['tag']['ignore-content'] ) ? (bool) $shortcode['tag']['ignore-content'] : false,
|
||||
'label' => isset( $shortcode['tag']['label'] ) ? $shortcode['tag']['label'] : '',
|
||||
'attributes' => [],
|
||||
];
|
||||
}
|
||||
if ( isset( $shortcode['attributes'] ) ) {
|
||||
foreach ( $shortcode['attributes'] as $attribute ) {
|
||||
$this->shortcodes[ $tag ]['attributes'][ $attribute['value'] ] = $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get_shortcodes() {
|
||||
return array_keys( $this->shortcodes );
|
||||
}
|
||||
|
||||
public function get_shortcode_attributes( $tag ) {
|
||||
return array_keys( $this->shortcodes[ $tag ]['attributes'] );
|
||||
}
|
||||
|
||||
public function get_shortcode_tag_encoding( $tag ) {
|
||||
return $this->shortcodes[ $tag ]['encoding'];
|
||||
}
|
||||
|
||||
public function get_shortcode_tag_encoding_condition( $tag ) {
|
||||
return $this->shortcodes[ $tag ]['encoding-condition'];
|
||||
}
|
||||
|
||||
public function get_shortcode_tag_type( $tag ) {
|
||||
if ( $this->shortcodes[ $tag ]['type'] ) {
|
||||
return strtoupper( $this->shortcodes[ $tag ]['type'] );
|
||||
}
|
||||
return 'VISUAL';
|
||||
}
|
||||
|
||||
public function get_shortcode_ignore_content( $tag ) {
|
||||
return $this->shortcodes[ $tag ]['ignore-content'];
|
||||
}
|
||||
|
||||
public function get_shortcode_attribute_encoding( $tag, $attribute ) {
|
||||
return $this->shortcodes[ $tag ]['attributes'][ $attribute ]['encoding'];
|
||||
}
|
||||
|
||||
public function get_shortcode_attribute_type( $tag, $attribute ) {
|
||||
if ( $this->shortcodes[ $tag ]['attributes'][ $attribute ]['type'] ) {
|
||||
return strtoupper( $this->shortcodes[ $tag ]['attributes'][ $attribute ]['type'] );
|
||||
}
|
||||
return 'LINE';
|
||||
}
|
||||
|
||||
public function get_shortcode_attribute_label( $tag, $attribute ) {
|
||||
$labelPath = 'content' === $attribute ? [ $tag, 'label' ] : [ $tag, 'attributes', $attribute, 'label' ];
|
||||
|
||||
return Obj::pathOr( '', $labelPath, $this->shortcodes );
|
||||
}
|
||||
|
||||
public function get_shortcode_parser() {
|
||||
return $this->factory->get_shortcode_parser( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_Post|stdClass $post
|
||||
*/
|
||||
public function register_strings( $post ) {
|
||||
if ( Gutenberg::doesNotHaveBlock( $post->post_content ) ) {
|
||||
$this->register_strings_in_content( $post->ID, $post->post_content, null );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $post_id
|
||||
* @param string $content
|
||||
* @param WPML\PB\Shortcode\StringCleanUp $stringCleanUp
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function register_strings_in_content( $post_id, $content, WPML\PB\Shortcode\StringCleanUp $stringCleanUp = null ) {
|
||||
$register_shortcodes = $this->factory->get_register_shortcodes( $this );
|
||||
|
||||
return $register_shortcodes->register_shortcode_strings( $post_id, $content, $stringCleanUp );
|
||||
}
|
||||
|
||||
public function set_factory( $factory ) {
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
public function get_package_key( $page_id ) {
|
||||
return array(
|
||||
'kind' => $this->get_package_kind(),
|
||||
'name' => $page_id,
|
||||
'title' => 'Page Builder Page ' . $page_id,
|
||||
'post_id' => $page_id,
|
||||
);
|
||||
}
|
||||
|
||||
public function get_package_kind() {
|
||||
return 'Page Builder ShortCode Strings';
|
||||
}
|
||||
|
||||
public function get_update_post( $package_data ) {
|
||||
return $this->factory->get_update_post( $package_data, $this );
|
||||
}
|
||||
|
||||
public function get_content_updater() {
|
||||
return $this->factory->get_shortcode_content_updater( $this );
|
||||
}
|
||||
|
||||
public function get_package_strings( $package_data ) {
|
||||
return $this->factory->get_string_translations( $this )->get_package_strings( $package_data );
|
||||
}
|
||||
|
||||
public function remove_string( $string_data ) {
|
||||
return $this->factory->get_string_translations( $this )->remove_string( $string_data );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
* @param object $post_content
|
||||
*/
|
||||
public function migrate_location( $post_id, $post_content ) {
|
||||
$migrate_locations = $this->factory->get_register_shortcodes( $this, true );
|
||||
$migrate_locations->register_shortcode_strings( $post_id, $post_content, null );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
class WPML_PB_Shortcodes {
|
||||
|
||||
/** @var WPML_PB_Shortcode_Strategy $shortcode_strategy */
|
||||
private $shortcode_strategy;
|
||||
|
||||
/** @var bool $is_wrapping_regular_text */
|
||||
private $is_wrapping_regular_text = false;
|
||||
|
||||
public function __construct( WPML_PB_Shortcode_Strategy $shortcode_strategy ) {
|
||||
$this->shortcode_strategy = $shortcode_strategy;
|
||||
}
|
||||
|
||||
public function get_shortcodes( $content ) {
|
||||
|
||||
$shortcodes = array();
|
||||
$pattern = get_shortcode_regex( $this->shortcode_strategy->get_shortcodes() );
|
||||
|
||||
if ( preg_match_all( '/' . $pattern . '/s', $content, $matches ) && isset( $matches[5] ) && ! empty( $matches[5] ) ) {
|
||||
for ( $index = 0; $index < sizeof( $matches[0] ); $index ++ ) {
|
||||
$shortcode = array(
|
||||
'block' => $matches[0][ $index ],
|
||||
'tag' => $matches[2][ $index ],
|
||||
'attributes' => $matches[3][ $index ],
|
||||
'content' => $matches[5][ $index ],
|
||||
);
|
||||
|
||||
$nested_shortcodes = array();
|
||||
if ( $shortcode['content'] ) {
|
||||
if ( $this->needs_wrapping_regular_text( $shortcode['content'] ) ) {
|
||||
$this->is_wrapping_regular_text = true;
|
||||
$shortcode['content'] = $this->wrap_regular_text( $shortcode['content'] );
|
||||
}
|
||||
|
||||
$nested_shortcodes = $this->get_shortcodes( $shortcode['content'] );
|
||||
$this->is_wrapping_regular_text = false;
|
||||
if ( count( $nested_shortcodes ) ) {
|
||||
$shortcode['content'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $nested_shortcodes ) ) {
|
||||
$shortcodes = array_merge( $shortcodes, $nested_shortcodes );
|
||||
}
|
||||
$shortcodes[] = $shortcode;
|
||||
}
|
||||
}
|
||||
|
||||
return $shortcodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wrap_regular_text( $content ) {
|
||||
$wrapper = new WPML_PB_Shortcode_Content_Wrapper( $content, $this->shortcode_strategy->get_shortcodes() );
|
||||
return $wrapper->get_wrapped_content();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function needs_wrapping_regular_text( $content ) {
|
||||
if ( $this->is_wrapping_regular_text ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WPML_PB_Shortcode_Content_Wrapper::isStrippedContentDifferent( $content );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
use WPML\LIB\WP\Gutenberg;
|
||||
|
||||
class WPML_PB_Update_Shortcodes_In_Content {
|
||||
|
||||
const LONG_STRING_THRESHOLD = 5000;
|
||||
|
||||
/** @var WPML_PB_Shortcode_Strategy $strategy */
|
||||
private $strategy;
|
||||
/** @var WPML_PB_Shortcode_Encoding $encoding */
|
||||
private $encoding;
|
||||
|
||||
private $new_content;
|
||||
private $string_translations;
|
||||
private $lang;
|
||||
|
||||
public function __construct( WPML_PB_Shortcode_Strategy $strategy, WPML_PB_Shortcode_Encoding $encoding ) {
|
||||
$this->strategy = $strategy;
|
||||
$this->encoding = $encoding;
|
||||
}
|
||||
|
||||
public function update( $translated_post_id, $original_post, $string_translations, $lang ) {
|
||||
if ( Gutenberg::hasBlock( $original_post->post_content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$original_content = $original_post->post_content;
|
||||
$original_content = apply_filters( 'wpml_pb_shortcode_content_for_translation', $original_content, $original_post->ID );
|
||||
|
||||
$new_translation = $this->update_content( $original_content, $string_translations, $lang );
|
||||
|
||||
$translated_post = get_post( $translated_post_id );
|
||||
$current_translation = isset( $translated_post->post_content ) ? $translated_post->post_content : '';
|
||||
$current_translation = apply_filters( 'wpml_pb_shortcode_content_for_translation', $current_translation, $translated_post_id );
|
||||
|
||||
$translation_saved = apply_filters( 'wpml_pb_shortcodes_save_translation', false, $translated_post_id, $new_translation );
|
||||
|
||||
if ( ! $translation_saved ) {
|
||||
if ( $new_translation != $original_content || '' === $current_translation ) {
|
||||
wpml_update_escaped_post(
|
||||
[
|
||||
'ID' => $translated_post_id,
|
||||
'post_content' => $new_translation,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function update_content( $original_content, $string_translations, $lang ) {
|
||||
$original_content = WPML_PB_Shortcode_Content_Wrapper::maybeWrap( $original_content, $this->strategy->get_shortcodes() );
|
||||
$this->new_content = $original_content;
|
||||
$this->string_translations = $string_translations;
|
||||
$this->lang = $lang;
|
||||
|
||||
$shortcode_parser = $this->strategy->get_shortcode_parser();
|
||||
$shortcodes = $shortcode_parser->get_shortcodes( $original_content );
|
||||
|
||||
foreach ( $shortcodes as $shortcode ) {
|
||||
$this->update_shortcodes( $shortcode );
|
||||
$this->update_shortcode_attributes( $shortcode );
|
||||
}
|
||||
|
||||
return WPML_PB_Shortcode_Content_Wrapper::unwrap( $this->new_content );
|
||||
}
|
||||
|
||||
private function update_shortcodes( $shortcode_data ) {
|
||||
$encoding = $this->strategy->get_shortcode_tag_encoding( $shortcode_data['tag'] );
|
||||
$translation = $this->get_translation( $shortcode_data['content'], $encoding );
|
||||
$this->replace_string_with_translation( $shortcode_data['block'], $shortcode_data['content'], $translation );
|
||||
}
|
||||
|
||||
private function update_shortcode_attributes( $shortcode_data ) {
|
||||
|
||||
$shortcode_attribute = $shortcode_data['attributes'];
|
||||
|
||||
$attributes = (array) shortcode_parse_atts( $shortcode_attribute );
|
||||
$translatable_attributes = $this->strategy->get_shortcode_attributes( $shortcode_data['tag'] );
|
||||
if ( ! empty( $attributes ) ) {
|
||||
foreach ( $attributes as $attr => $attr_value ) {
|
||||
if ( in_array( $attr, $translatable_attributes, true ) ) {
|
||||
$encoding = $this->strategy->get_shortcode_attribute_encoding( $shortcode_data['tag'], $attr );
|
||||
$translation = $this->get_translation( $attr_value, $encoding );
|
||||
$translation = $this->filter_attribute_translation( $translation, $encoding );
|
||||
$shortcode_attribute = $this->replace_string_with_translation( $shortcode_attribute, $attr_value, $translation, true, $attr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function replace_string_with_translation( $block, $original, $translation, $is_attribute = false, $attr = '' ) {
|
||||
$translation = apply_filters( 'wpml_pb_before_replace_string_with_translation', $translation, $is_attribute );
|
||||
$used_wrapper = false;
|
||||
$new_block = $block;
|
||||
|
||||
if ( $translation ) {
|
||||
|
||||
if ( $this->is_string_too_long_for_regex( $original ) ) {
|
||||
$block = WPML_PB_Shortcode_Content_Wrapper::unwrap( $block );
|
||||
$new_block = str_replace( $original, $translation, $block );
|
||||
$this->new_content = str_replace( $block, $new_block, $this->new_content );
|
||||
} else {
|
||||
if ( $is_attribute && $attr ) {
|
||||
$pattern = '/' . $attr . '=(["\'])' . preg_quote( $original, '/' ) . '(["\'])/';
|
||||
$replacement = $attr . '=${1}' . $this->escape_backward_reference_on_replacement_string( $translation ) . '${2}';
|
||||
} else {
|
||||
$used_wrapper = false !== strpos( $new_block, '[' . WPML_PB_Shortcode_Content_Wrapper::WRAPPER_SHORTCODE_NAME . ']' );
|
||||
$pattern = '/(]\s*)' . preg_quote( trim( $original ), '/' ) . '(\s*\[)/';
|
||||
$replacement = '${1}' . $this->escape_backward_reference_on_replacement_string( trim( $translation ) ) . '${2}';
|
||||
}
|
||||
|
||||
$new_block = preg_replace( $pattern, $replacement, $block );
|
||||
$block = WPML_PB_Shortcode_Content_Wrapper::unwrap( $block );
|
||||
$new_block = WPML_PB_Shortcode_Content_Wrapper::unwrap( $new_block );
|
||||
$replacement = $this->escape_backward_reference_on_replacement_string( $new_block );
|
||||
|
||||
if ( $used_wrapper ) {
|
||||
$this->new_content = $this->replace_content_without_delimiters( $block, $replacement );
|
||||
} else {
|
||||
$this->new_content = preg_replace( '/' . preg_quote( $block, '/' ) . '/', $replacement, $this->new_content, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $new_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to escape backward references that could be included in the replacement text
|
||||
* e.g. '$1999.each' => '$19' is considered as a backward reference
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function escape_backward_reference_on_replacement_string( $string ) {
|
||||
return preg_replace( '/\$([\d]{1,2})/', '\\\$' . '${1}', $string );
|
||||
}
|
||||
|
||||
private function replace_content_without_delimiters( $block, $replacement ) {
|
||||
return preg_replace( '/(]\s*)' . preg_quote( $block, '/' ) . '(\s*\[)/', '${1}' . $replacement . '${2}', $this->new_content, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_string_too_long_for_regex( $string ) {
|
||||
return mb_strlen( $string ) > self::LONG_STRING_THRESHOLD;
|
||||
}
|
||||
|
||||
private function get_translation( $original, $encoding = false ) {
|
||||
$decoded_original = $this->encoding->decode( $original, $encoding );
|
||||
|
||||
$translation = null;
|
||||
|
||||
if ( is_array( $decoded_original ) ) {
|
||||
|
||||
$translation = array();
|
||||
foreach ( $decoded_original as $key => $data ) {
|
||||
if ( $data['translate'] ) {
|
||||
$translated_data = $this->get_translation( $data['value'], '' );
|
||||
if ( $translated_data ) {
|
||||
$translation[ $key ] = $translated_data;
|
||||
} else {
|
||||
$translation[ $key ] = $data['value'];
|
||||
}
|
||||
} else {
|
||||
$translation[ $key ] = $data['value'];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$string_name = md5( $decoded_original );
|
||||
if ( isset( $this->string_translations[ $string_name ][ $this->lang ] ) && $this->string_translations[ $string_name ][ $this->lang ]['status'] == ICL_TM_COMPLETE ) {
|
||||
$translation = $this->string_translations[ $string_name ][ $this->lang ]['value'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( $translation ) {
|
||||
$translation = $this->encoding->encode( $translation, $encoding );
|
||||
}
|
||||
|
||||
return $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $translation
|
||||
* @param string $encoding
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function filter_attribute_translation( $translation, $encoding ) {
|
||||
if ( 'allow_html_tags' !== $encoding ) {
|
||||
$translation = htmlspecialchars( htmlspecialchars_decode( $translation ) );
|
||||
}
|
||||
|
||||
$translation = str_replace( array( '[', ']' ), array( '[', ']' ), $translation );
|
||||
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user