first commit

This commit is contained in:
2023-09-12 21:41:04 +02:00
commit 3361a7f053
13284 changed files with 2116755 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
<?php
class WPML_Media_Add_To_Translation_Package_Factory implements IWPML_Backend_Action_Loader {
public function create() {
return new WPML_Media_Add_To_Translation_Package( new WPML_Media_Post_With_Media_Files_Factory() );
}
}

View File

@@ -0,0 +1,147 @@
<?php
class WPML_Media_Add_To_Translation_Package implements IWPML_Action {
const ALT_PLACEHOLDER = '{%ALT_TEXT%}';
const CAPTION_PLACEHOLDER = '{%CAPTION%}';
/** @var WPML_Media_Post_With_Media_Files_Factory $post_media_factory */
private $post_media_factory;
public function __construct( WPML_Media_Post_With_Media_Files_Factory $post_media_factory ) {
$this->post_media_factory = $post_media_factory;
}
public function add_hooks() {
add_action( 'wpml_tm_translation_job_data', array( $this, 'add_media_strings' ), PHP_INT_MAX, 2 );
}
public function add_media_strings( $package, $post ) {
$basket = TranslationProxy_Basket::get_basket( true );
$bundled_media_data = $this->get_bundled_media_to_translate( $post );
if ( $bundled_media_data ) {
foreach ( $bundled_media_data as $attachment_id => $data ) {
foreach ( $data as $field => $value ) {
if (
isset( $basket['post'][ $post->ID ]['media-translation'] ) &&
! in_array( $attachment_id, $basket['post'][ $post->ID ]['media-translation'] )
) {
$options = array(
'translate' => 0,
'data' => true,
'format' => '',
);
} else {
$options = array(
'translate' => 1,
'data' => base64_encode( $value ),
'format' => 'base64',
);
}
$package['contents'][ 'media_' . $attachment_id . '_' . $field ] = $options;
}
if (
isset( $basket['post'][ $post->ID ]['media-translation'] ) &&
in_array( $attachment_id, $basket['post'][ $post->ID ]['media-translation'] )
) {
$package['contents'][ 'should_translate_media_image_' . $attachment_id ] = array(
'translate' => 0,
'data' => true,
'format' => '',
);
}
}
$package = $this->add_placeholders_for_duplicate_fields( $package, $bundled_media_data );
}
return $package;
}
private function get_bundled_media_to_translate( $post ) {
$post_media = $this->post_media_factory->create( $post->ID );
$bundled_media_data = array();
foreach ( $post_media->get_media_ids() as $attachment_id ) {
$attachment = get_post( $attachment_id );
if ( $attachment->post_title ) {
$bundled_media_data[ $attachment_id ]['title'] = $attachment->post_title;
}
if ( $attachment->post_excerpt ) {
$bundled_media_data[ $attachment_id ]['caption'] = $attachment->post_excerpt;
}
if ( $attachment->post_content ) {
$bundled_media_data[ $attachment_id ]['description'] = $attachment->post_content;
}
if ( $alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) {
$bundled_media_data[ $attachment_id ]['alt_text'] = $alt;
}
}
return $bundled_media_data;
}
private function add_placeholders_for_duplicate_fields( $package, $bundled_media_data ) {
$caption_parser = new WPML_Media_Caption_Tags_Parse();
foreach ( $package['contents'] as $field => $data ) {
if ( $data['translate'] && 'base64' === $data['format'] ) {
$original = $content = base64_decode( $data['data'] );
$captions = $caption_parser->get_captions( $content );
foreach ( $captions as $caption ) {
$caption_id = $caption->get_id();
$caption_shortcode = $new_caption_shortcode = $caption->get_shortcode_string();
if ( isset( $bundled_media_data[ $caption_id ] ) ) {
if ( isset( $bundled_media_data[ $caption_id ]['caption'] ) && $bundled_media_data[ $caption_id ]['caption'] === $caption->get_caption() ) {
$new_caption_shortcode = $this->replace_caption_with_placeholder( $new_caption_shortcode, $caption );
}
if ( isset( $bundled_media_data[ $caption_id ]['alt_text'] ) && $bundled_media_data[ $caption_id ]['alt_text'] === $caption->get_image_alt() ) {
$new_caption_shortcode = $this->replace_alt_text_with_placeholder( $new_caption_shortcode, $caption );
}
if ( $new_caption_shortcode !== $caption_shortcode ) {
$content = str_replace( $caption_shortcode, $new_caption_shortcode, $content );
}
}
}
if ( $content !== $original ) {
$package['contents'][ $field ]['data'] = base64_encode( $content );
}
}
}
return $package;
}
private function replace_caption_with_placeholder( $caption_shortcode, WPML_Media_Caption $caption ) {
$caption_content = $caption->get_content();
$search_pattern = '/(>\s?)(' . preg_quote( $caption->get_caption(), '/' ) . ')/';
$new_caption_content = preg_replace( $search_pattern, '$1' . self::CAPTION_PLACEHOLDER, $caption_content, 1 );
return str_replace( $caption_content, $new_caption_content, $caption_shortcode );
}
private function replace_alt_text_with_placeholder( $caption_shortcode, WPML_Media_Caption $caption ) {
$alt_text = $caption->get_image_alt();
return str_replace( 'alt="' . $alt_text . '"', 'alt="' . self::ALT_PLACEHOLDER . '"', $caption_shortcode );
}
}

View File

@@ -0,0 +1,13 @@
<?php
/**
* Class WPML_Media_Attachment_Image_Update_Factory
*/
class WPML_Media_Attachment_Image_Update_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $wpdb;
return new WPML_Media_Attachment_Image_Update( $wpdb );
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Class WPML_Media_Attachment_Image_Update
* Allows adding a custom image to a translated attachment
*/
class WPML_Media_Attachment_Image_Update implements IWPML_Action {
const TRANSIENT_FILE_UPLOAD_PREFIX = 'wpml_media_file_update_';
/**
* @var wpdb
*/
private $wpdb;
/**
* WPML_Media_Attachment_Image_Update constructor.
*
* @param wpdb $wpdb
*/
public function __construct( wpdb $wpdb ) {
$this->wpdb = $wpdb;
}
public function add_hooks() {
add_action( 'wp_ajax_wpml_media_upload_file', array( $this, 'handle_upload' ) );
}
public function handle_upload() {
if ( $this->is_valid_action() ) {
$original_attachment_id = (int) $_POST['original-attachment-id'];
$attachment_id = (int) $_POST['attachment-id'];
$file_array = $_FILES['file'];
$target_language = $_POST['language'];
$thumb_path = '';
$thumb_url = '';
$upload_overrides = apply_filters( 'wpml_media_wp_upload_overrides', array( 'test_form' => false ) );
$file = wp_handle_upload( $file_array, $upload_overrides );
if ( ! isset( $file['error'] ) ) {
if ( wp_image_editor_supports( array( 'mime_type' => $file['type'] ) ) ) {
$editor = wp_get_image_editor( $file['file'] );
if ( ! is_wp_error( $editor ) ) {
$size = $editor->get_size();
if ( $size['width'] > 150 || $size['height'] > 150 ) {
$resizing = $editor->resize( 150, 150, true );
if ( is_wp_error( $resizing ) ) {
wp_send_json_error( $resizing->get_error_message() );
}
}
$thumb = $editor->save();
if ( ! empty( $thumb ) ) {
$uploads_dir = wp_get_upload_dir();
$thumb_url = $uploads_dir['baseurl'] . $uploads_dir['subdir'] . '/' . $thumb['file'];
$thumb_path = $thumb['path'];
}
} else {
wp_send_json_error( __( 'Failed to load the image editor', 'wpml-media' ) );
}
} elseif ( 0 === strpos( $file['type'], 'image/' ) ) {
$thumb_url = $file['url'];
$thumb_path = $file['file'];
} else {
$thumb_url = wp_mime_type_icon( $original_attachment_id );
}
set_transient(
self::TRANSIENT_FILE_UPLOAD_PREFIX . $original_attachment_id . '_' . $target_language,
array(
'upload' => $file,
'thumb' => $thumb_path,
),
HOUR_IN_SECONDS
);
wp_send_json_success(
array(
'attachment_id' => $attachment_id,
'thumb' => $thumb_url,
'name' => basename( $file['file'] ),
)
);
} else {
wp_send_json_error( $file['error'] );
}
} else {
wp_send_json_error( 'invalid action' );
}
}
private function is_valid_action() {
$is_attachment_id = isset( $_POST['attachment-id'] );
$is_post_action = isset( $_POST['action'] ) && 'wpml_media_upload_file' === $_POST['action'];
return $is_attachment_id && $is_post_action && wp_verify_nonce( $_POST['wpnonce'], 'media-translation' );
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Class WPML_Media_Caption_Tags_Parse
*/
class WPML_Media_Caption_Tags_Parse {
/**
* @param string $text
*
* @return array
*/
public function get_captions( $text ) {
$captions = array();
if ( preg_match_all( '/\[caption (.+)\](.+)\[\/caption\]/sU', $text, $matches ) ) {
for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
$captions[] = new WPML_Media_Caption( $matches[0][ $i ], $matches[1][ $i ], $matches[2][ $i ] );
}
}
return $captions;
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Class WPML_Media_Caption
*/
class WPML_Media_Caption {
private $shortcode;
private $content_string;
private $attributes;
private $attachment_id;
private $link;
private $img;
private $caption;
public function __construct( $caption_shortcode, $attributes_data, $content_string ) {
$this->shortcode = $caption_shortcode;
$this->content_string = $content_string;
$this->attributes = $this->find_attributes_array( $attributes_data );
$this->attachment_id = $this->find_attachment_id( $this->attributes );
$this->link = $this->find_link( $content_string );
$img_parser = new WPML_Media_Img_Parse();
$this->img = current( $img_parser->get_imgs( $content_string ) );
$this->caption = trim( strip_tags( $content_string ) );
}
/**
* @return int
*/
public function get_id() {
return $this->attachment_id;
}
public function get_caption() {
return $this->caption;
}
public function get_shortcode_string() {
return $this->shortcode;
}
public function get_content() {
return $this->content_string;
}
public function get_image_alt() {
if ( isset( $this->img['attributes']['alt'] ) ) {
return $this->img['attributes']['alt'];
} else {
return '';
}
}
public function get_link() {
return $this->link;
}
/**
* @param string $attributes_list
*
* @return array
*/
private function find_attributes_array( $attributes_list ) {
$attributes = array();
if ( preg_match_all( '/(\S+)=["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?/', $attributes_list, $attribute_matches ) ) {
foreach ( $attribute_matches[1] as $k => $key ) {
$attributes[ $key ] = $attribute_matches[2][ $k ];
}
}
return $attributes;
}
/**
* @param array $attributes
*
* @return null|int
*/
private function find_attachment_id( $attributes ) {
$attachment_id = null;
if ( isset( $attributes['id'] ) ) {
if ( preg_match( '/attachment_([0-9]+)\b/', $attributes['id'], $id_match ) ) {
if ( 'attachment' === get_post_type( (int) $id_match[1] ) ) {
$attachment_id = (int) $id_match[1];
}
}
}
return $attachment_id;
}
/**
* @param $string
*
* @return array
*/
private function find_link( $string ) {
$link = array();
if ( preg_match( '/<a ([^>]+)>(.+)<\/a>/s', $string, $a_match ) ) {
if ( preg_match( '/href=["\']([^"]+)["\']/', $a_match[1], $url_match ) ) {
$link['url'] = $url_match[1];
}
}
return $link;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Class WPML_Media_Custom_Field_Images_Translation_Factory
*/
class WPML_Media_Custom_Field_Images_Translation_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $sitepress, $iclTranslationManagement;
$media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
if ( $media_localization_settings['custom_fields'] || WPML_Media_Custom_Field_Batch_Url_Translation::is_ajax_request() ) {
$image_translator = new WPML_Media_Image_Translate( $sitepress, new WPML_Media_Attachment_By_URL_Factory() );
$image_updater = new WPML_Media_Translated_Images_Update( new WPML_Media_Img_Parse(), $image_translator, new WPML_Media_Sizes() );
return new WPML_Media_Custom_Field_Images_Translation( $image_updater, $sitepress, $iclTranslationManagement );
}
return null;
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* Class WPML_Media_Custom_Field_Images_Translation
* Translate images in posts custom fields translations when a custom field is created or updated
*/
class WPML_Media_Custom_Field_Images_Translation implements IWPML_Action {
/**
* @var WPML_Media_Custom_Field_Images_Translation
*/
private $images_updater;
/**
* @var SitePress
*/
private $sitepress;
/**
* @var TranslationManagement
*/
private $iclTranslationManagement;
/**
* WPML_Media_Custom_Field_Images_Translation constructor.
*
* @param WPML_Media_Translated_Images_Update $images_updater
* @param SitePress $sitepress
* @param TranslationManagement $iclTranslationManagement
*/
public function __construct(
WPML_Media_Translated_Images_Update $images_updater,
SitePress $sitepress,
TranslationManagement $iclTranslationManagement
) {
$this->images_updater = $images_updater;
$this->sitepress = $sitepress;
$this->iclTranslationManagement = $iclTranslationManagement;
}
public function add_hooks() {
add_action( 'updated_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX, 4 );
add_action( 'added_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX, 4 );
}
/**
* @param int $meta_id
* @param int $object_id
* @param string $meta_key
* @param string $meta_value
*/
public function translate_images( $meta_id, $object_id, $meta_key, $meta_value ) {
$settings_factory = new WPML_Custom_Field_Setting_Factory( $this->iclTranslationManagement );
$setting = $settings_factory->post_meta_setting( $meta_key );
$is_custom_field_translatable = $this->sitepress->get_wp_api()
->constant( 'WPML_TRANSLATE_CUSTOM_FIELD' ) === $setting->status();
$post_type = get_post_type( $object_id );
$is_post_translatable = $this->sitepress->is_translated_post_type( $post_type );
if ( is_string( $meta_value ) && $is_post_translatable && $is_custom_field_translatable ) {
$post_element = new WPML_Post_Element( $object_id, $this->sitepress );
$source_language = $post_element->get_source_language_code();
if ( null !== $source_language ) {
$this->filter_meta_value_and_update(
$meta_value,
$meta_key,
$post_element->get_language_code(),
$source_language,
$object_id
);
} else {
foreach ( array_keys( $this->sitepress->get_active_languages() ) as $language ) {
$translation = $post_element->get_translation( $language );
if ( $translation ) {
$this->filter_meta_value_and_update(
$meta_value,
$meta_key,
$language,
$source_language,
$translation->get_id()
);
}
}
}
}
}
/**
* @param string $meta_value
* @param string $meta_key
* @param string $target_language
* @param string $source_language
* @param int $post_id
*
* @return string
*/
private function filter_meta_value_and_update( $meta_value, $meta_key, $target_language, $source_language, $post_id ) {
$meta_value_filtered = $this->images_updater->replace_images_with_translations(
$meta_value,
$target_language,
$source_language
);
if ( $meta_value_filtered !== $meta_value ) {
remove_action( 'updated_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX );
update_post_meta( $post_id, $meta_key, wp_slash( $meta_value_filtered ), $meta_value );
add_action( 'updated_post_meta', array( $this, 'translate_images' ), PHP_INT_MAX, 4 );
}
return $meta_value_filtered;
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* Class WPML_Media_Image_Translate
* Allows getting translated images in a give language from an attachment
*/
class WPML_Media_Image_Translate {
/**
* @var SitePress
*/
private $sitepress;
/**
* @var WPML_Media_Attachment_By_URL_Factory
*/
private $attachment_by_url_factory;
/**
* WPML_Media_Image_Translate constructor.
*
* @param SitePress $sitepress
* @param WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory
*/
public function __construct( SitePress $sitepress, WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory ) {
$this->sitepress = $sitepress;
$this->attachment_by_url_factory = $attachment_by_url_factory;
}
/**
* @param int $attachment_id
* @param string $language
* @param string $size
*
* @return string
*/
public function get_translated_image( $attachment_id, $language, $size = null ) {
$image_url = '';
$attachment = new WPML_Post_Element( $attachment_id, $this->sitepress );
$attachment_translation = $attachment->get_translation( $language );
if ( $attachment_translation ) {
$uploads_dir = wp_get_upload_dir();
$attachment_id = $attachment_translation->get_id();
if ( null === $size ) {
$image_url = $uploads_dir['baseurl'] . '/' . get_post_meta( $attachment_id, '_wp_attached_file', true );
} else {
$image_url = $this->get_sized_image_url( $attachment_id, $size, $uploads_dir );
}
}
return $image_url;
}
/**
* @param string $img_src
* @param string $source_language
* @param string $target_language
*
* @return string|bool
*/
public function get_translated_image_by_url( $img_src, $source_language, $target_language ) {
$attachment_id = $this->get_attachment_id_by_url( $img_src, $source_language );
if ( $attachment_id ) {
$size = $this->get_image_size_from_url( $img_src, $attachment_id );
try {
$img_src = $this->get_translated_image( $attachment_id, $target_language, $size );
} catch ( Exception $e ) {
$img_src = false;
}
} else {
$img_src = false;
}
return $img_src;
}
/**
* @param string $img_src
* @param string $source_language
*
* @return int
*/
public function get_attachment_id_by_url( $img_src, $source_language ) {
$attachment_by_url = $this->attachment_by_url_factory->create( $img_src, $source_language );
return (int) $attachment_by_url->get_id();
}
/**
* @param string $url
* @param int $attachment_id
*
* @return string
*/
private function get_image_size_from_url( $url, $attachment_id ) {
$media_sizes = new WPML_Media_Sizes();
return $media_sizes->get_image_size_from_url( $url, $attachment_id );
}
/**
* @param $attachment_id
* @param $size
* @param $uploads_dir
*
* @return string
*/
private function get_sized_image_url( $attachment_id, $size, $uploads_dir ) {
$image_url = '';
$meta_data = wp_get_attachment_metadata( $attachment_id );
if ( array_key_exists( $size, $meta_data['sizes'] ) ) {
$image_url_parts = array( $uploads_dir['baseurl'] );
if ( array_key_exists( 'file', $meta_data ) ) {
$file_subdirectory = $meta_data['file'];
$file_subdirectory_parts = explode( '/', $file_subdirectory );
array_pop( $file_subdirectory_parts );
$image_url_parts[] = implode( '/', $file_subdirectory_parts );
}
$image_url_parts[] = $meta_data['sizes'][ $size ]['file'];
$image_url = implode( '/', $image_url_parts );
}
return $image_url;
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* Class WPML_Media_Img_Parse
*/
class WPML_Media_Img_Parse {
/**
* @param string $text
*
* @return array
*/
public function get_imgs( $text ) {
$images = $this->get_from_img_tags( $text );
if ( $this->can_parse_blocks( $text ) ) {
$blocks = parse_blocks( $text );
$images = array_merge( $images, $this->get_from_css_background_images_in_blocks( $blocks ) );
} else {
$images = array_merge( $images, $this->get_from_css_background_images( $text ) );
}
return $images;
}
/**
* @param string $text
*
* @return array
*/
private function get_from_img_tags( $text ) {
$images = array();
if ( preg_match_all( '/<img ([^>]+)>/s', $text, $matches ) ) {
foreach ( $matches[1] as $i => $match ) {
if ( preg_match_all( '/(\S+)\\s*=\\s*["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?/', $match, $attribute_matches ) ) {
$attributes = array();
foreach ( $attribute_matches[1] as $k => $key ) {
$attributes[ $key ] = $attribute_matches[2][ $k ];
}
if ( isset( $attributes['src'] ) ) {
$images[ $i ]['attributes'] = $attributes;
$images[ $i ]['attachment_id'] = $this->get_attachment_id_from_attributes( $images[ $i ]['attributes'] );
}
}
}
}
return $images;
}
/**
* @param string $text
*
* @return array
*/
private function get_from_css_background_images( $text ) {
$images = array();
if ( preg_match_all( '/<\w+[^>]+style\s?=\s?"[^"]*?background-image:url\(\s?([^\s\)]+)\s?\)/', $text, $matches ) ) {
foreach ( $matches[1] as $src ) {
$images[] = array(
'attributes' => array( 'src' => $src ),
'attachment_id' => null,
);
}
}
return $images;
}
/**
* @param array $blocks
*
* @return array
*/
private function get_from_css_background_images_in_blocks( $blocks ) {
$images = array();
foreach ( $blocks as $block ) {
$block = $this->sanitize_block( $block );
if ( ! empty( $block->innerBlocks ) ) {
$inner_images = $this->get_from_css_background_images_in_blocks( $block->innerBlocks );
$images = array_merge( $images, $inner_images );
continue;
}
if ( ! isset( $block->innerHTML, $block->attrs->id ) ) {
continue;
}
$background_images = $this->get_from_css_background_images( $block->innerHTML );
$image = reset( $background_images );
if ( $image ) {
$image['attachment_id'] = $block->attrs->id;
$images[] = $image;
}
}
return $images;
}
/**
* `parse_blocks` does not specify which kind of collection it should return
* (not always an array of `WP_Block_Parser_Block`) and the block parser can be filtered,
* so we'll cast it to a standard object for now.
*
* @param mixed $block
*
* @return stdClass|WP_Block_Parser_Block
*/
private function sanitize_block( $block ) {
$block = (object) $block;
if ( isset( $block->attrs ) ) {
/** Sometimes `$block->attrs` is an object or an array, so we'll use an object */
$block->attrs = (object) $block->attrs;
}
return $block;
}
function can_parse_blocks( $string ) {
return false !== strpos( $string, '<!-- wp:' ) && function_exists( 'parse_blocks' );
}
/**
* @param $attributes
*
* @return null|int
*/
private function get_attachment_id_from_attributes( $attributes ) {
$attachment_id = null;
if ( isset( $attributes['class'] ) ) {
if ( preg_match( '/wp-image-([0-9]+)\b/', $attributes['class'], $id_match ) ) {
if ( 'attachment' === get_post_type( (int) $id_match[1] ) ) {
$attachment_id = (int) $id_match[1];
}
}
}
return $attachment_id;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* Class WPML_Media_Post_Images_Translation_Factory
*/
class WPML_Media_Post_Images_Translation_Factory implements IWPML_REST_Action_Loader, IWPML_Backend_Action_Loader {
/**
* @return IWPML_Action|null|WPML_Media_Post_Images_Translation
*/
public function create() {
global $wpdb, $sitepress;
$media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
if ( $media_localization_settings['posts'] ) {
$image_translator = new WPML_Media_Image_Translate( $sitepress, new WPML_Media_Attachment_By_URL_Factory() );
$image_updater = new WPML_Media_Translated_Images_Update( new WPML_Media_Img_Parse(), $image_translator, new WPML_Media_Sizes() );
return new WPML_Media_Post_Images_Translation(
$image_updater,
$sitepress,
$wpdb,
new WPML_Translation_Element_Factory( $sitepress ),
new WPML_Media_Custom_Field_Images_Translation_Factory(),
new WPML_Media_Usage_Factory()
);
}
return null;
}
}

View File

@@ -0,0 +1,336 @@
<?php
/**
* Class WPML_Media_Post_Images_Translation
* Translate images in posts translations when a post is created or updated
*/
class WPML_Media_Post_Images_Translation implements IWPML_Action {
/**
* @var WPML_Media_Translated_Images_Update
*/
private $images_updater;
/**
* @var SitePress
*/
private $sitepress;
/**
* @var wpdb
*/
private $wpdb;
/**
* @var WPML_Translation_Element_Factory
*/
private $translation_element_factory;
/**
* @var WPML_Media_Custom_Field_Images_Translation_Factory
*/
private $custom_field_images_translation_factory;
/**
* @var WPML_Media_Usage_Factory
*/
private $media_usage_factory;
private $captions_map = array();
private $translate_locks = array();
public function __construct(
WPML_Media_Translated_Images_Update $images_updater,
SitePress $sitepress,
wpdb $wpdb,
WPML_Translation_Element_Factory $translation_element_factory,
WPML_Media_Custom_Field_Images_Translation_Factory $custom_field_images_translation_factory,
WPML_Media_Usage_Factory $media_usage_factory
) {
$this->images_updater = $images_updater;
$this->sitepress = $sitepress;
$this->wpdb = $wpdb;
$this->translation_element_factory = $translation_element_factory;
$this->custom_field_images_translation_factory = $custom_field_images_translation_factory;
$this->media_usage_factory = $media_usage_factory;
}
public function add_hooks() {
add_action( 'save_post', array( $this, 'translate_images' ), PHP_INT_MAX, 1 );
add_filter( 'wpml_pre_save_pro_translation', array( $this, 'translate_images_in_content' ), PHP_INT_MAX, 2 );
add_filter( 'wpml_pre_save_pro_translation', array(
$this,
'replace_placeholders_and_id_in_caption_shortcode'
), PHP_INT_MAX, 2 );
add_action( 'wpml_tm_job_fields',
array( $this, 'replace_caption_placeholders_in_fields' ), 10, 2 );
add_filter( 'wpml_tm_job_data_post_content', array(
$this,
'restore_placeholders_in_translated_job_body'
), 10, 1 );
add_action( 'icl_make_duplicate', array( $this, 'translate_images_in_duplicate' ), PHP_INT_MAX, 4 );
add_action( 'wpml_added_media_file_translation', array( $this, 'translate_url_in_post' ), PHP_INT_MAX, 1 );
add_action( 'wpml_pro_translation_completed', array( $this, 'translate_images' ), PHP_INT_MAX, 1 );
add_action( 'wpml_restored_media_file_translation', array( $this, 'translate_url_in_post' ), PHP_INT_MAX, 2 );
}
/**
* @param int $post_id
*/
public function translate_images( $post_id ) {
if ( isset( $this->translate_locks[ $post_id ] ) ) {
return;
}
$this->translate_locks[ $post_id ] = true;
$post = get_post( $post_id );
if ( ! $post ) {
return;
}
/** @var WPML_Post_Element $post_element */
$post_element = $this->translation_element_factory->create( $post_id, 'post' );
$source_language = $post_element->get_source_language_code();
if ( null !== $source_language ) {
$this->translate_images_in_post_content( $post, $post_element );
$this->translate_images_in_custom_fields( $post_id );
} else { // is original
foreach ( array_keys( $this->sitepress->get_active_languages() ) as $target_language ) {
/** @var WPML_Post_Element $translation */
$translation = $post_element->get_translation( $target_language );
if ( null !== $translation && $post_id !== $translation->get_id() ) {
$this->translate_images_in_post_content( get_post( $translation->get_id() ), $translation );
$this->translate_images_in_custom_fields( $translation->get_id() );
}
}
}
unset( $this->translate_locks[ $post_id ] );
}
/**
* @param int $master_post_id
* @param string $language
* @param array $post_array
* @param int $post_id
*/
public function translate_images_in_duplicate( $master_post_id, $language, $post_array, $post_id ) {
$this->translate_images( $post_id );
}
/**
* @param WP_Post $post
* @param WPML_Post_Element $post_element
*/
private function translate_images_in_post_content( WP_Post $post, WPML_Post_Element $post_element ) {
if ( (bool) apply_filters( 'wpml_pb_should_body_be_translated', true, $post, 'translate_images_in_post_content' ) ) {
$post_content_filtered = $this->images_updater->replace_images_with_translations(
$post->post_content,
$post_element->get_language_code(),
$post_element->get_source_language_code()
);
if ( $post_content_filtered !== $post->post_content ) {
$this->wpdb->update(
$this->wpdb->posts,
array( 'post_content' => $post_content_filtered ),
array( 'ID' => $post->ID ),
array( '%s' ),
array( '%d' )
);
}
} elseif ( $this->is_updated_from_media_translation_menu() ) {
do_action( 'wpml_pb_resave_post_translation', $post_element );
}
}
private function is_updated_from_media_translation_menu() {
$allowed_actions = array(
'wpml_media_save_translation',
'wpml_media_translate_media_url_in_posts',
);
return isset( $_POST['action'] ) && in_array( $_POST['action'], $allowed_actions, true );
}
/**
* @param int $post_id
*/
private function translate_images_in_custom_fields( $post_id ) {
$custom_fields_image_translation = $this->custom_field_images_translation_factory->create();
if ( $custom_fields_image_translation ) {
$post_meta = get_metadata( 'post', $post_id );
foreach ( $post_meta as $meta_key => $meta_value ) {
$custom_fields_image_translation->translate_images( null, $post_id, $meta_key, $meta_value[0] );
}
}
}
/**
* @param array $postarr
* @param stdClass $job
*
* @return array
*/
public function translate_images_in_content( array $postarr, stdclass $job ) {
$postarr['post_content'] = $this->images_updater->replace_images_with_translations(
$postarr['post_content'],
$job->language_code,
$job->source_language_code
);
return $postarr;
}
public function translate_url_in_post( $attachment_id, $posts = array() ) {
if ( ! $posts ) {
$media_usage = $this->media_usage_factory->create( $attachment_id );
$posts = $media_usage->get_posts();
}
foreach ( $posts as $post_id ) {
$this->translate_images( $post_id );
}
}
public function replace_placeholders_and_id_in_caption_shortcode( array $postarr, stdClass $job ) {
$media = $this->find_media_in_job( $job );
$postarr['post_content'] = $this->replace_caption_placeholders_in_string(
$postarr['post_content'],
$media,
$job->language_code
);
return $postarr;
}
public function replace_caption_placeholders_in_string( $text, $media, $language ) {
$caption_parser = new WPML_Media_Caption_Tags_Parse();
$captions = $caption_parser->get_captions( $text );
foreach ( $captions as $caption ) {
$attachment_id = $caption->get_id();
$caption_shortcode = $new_caption_shortcode = $caption->get_shortcode_string();
if ( isset( $media[ $attachment_id ] ) ) {
if ( isset( $media[ $attachment_id ]['caption'] ) ) {
$new_caption_shortcode = $this->replace_placeholder_with_caption( $new_caption_shortcode, $caption, $media[ $attachment_id ]['caption'] );
}
if ( isset( $media[ $attachment_id ]['alt'] ) ) {
$new_caption_shortcode = $this->replace_placeholder_with_alt_text( $new_caption_shortcode, $caption, $media[ $attachment_id ]['alt'] );
}
}
if ( $attachment_id ) {
$new_caption_shortcode = $this->replace_caption_id_with_translated_id(
$new_caption_shortcode,
$attachment_id,
$language
);
}
if ( $new_caption_shortcode !== $caption_shortcode ) {
$text = str_replace( $caption_shortcode, $new_caption_shortcode, $text );
$this->captions_map[ $caption_shortcode ] = $new_caption_shortcode;
}
}
return $text;
}
/**
* @param int $new_post_id
* @param array $fields
* @param stdClass $job
*/
public function replace_caption_placeholders_in_fields( array $fields, stdClass $job ) {
$media = $this->find_media_in_job( $job );
foreach ( $fields as $field_id => $field ) {
$fields[ $field_id ]['data'] = $this->replace_caption_placeholders_in_string(
$field['data'],
$media,
$job->language_code
);
}
return $fields;
}
private function replace_placeholder_with_caption( $caption_shortcode, WPML_Media_Caption $caption, $new_caption ) {
$caption_content = $caption->get_content();
$new_caption_content = str_replace( WPML_Media_Add_To_Translation_Package::CAPTION_PLACEHOLDER, $new_caption, $caption_content );
return str_replace( $caption_content, $new_caption_content, $caption_shortcode );
}
private function replace_placeholder_with_alt_text( $caption_shortcode, WPML_Media_Caption $caption, $new_alt_text ) {
return str_replace( 'alt="' . WPML_Media_Add_To_Translation_Package::ALT_PLACEHOLDER . '"', 'alt="' . $new_alt_text . '"', $caption_shortcode );
}
private function replace_caption_id_with_translated_id( $caption_shortcode, $attachment_id, $language ) {
$post_element = $this->translation_element_factory->create( $attachment_id, 'post' );
$translation = $post_element->get_translation( $language );
if ( $translation ) {
$translated_id = $translation->get_id();
$caption_shortcode = str_replace(
'id="attachment_' . $attachment_id . '"',
'id="attachment_' . $translated_id . '"',
$caption_shortcode
);
}
return $caption_shortcode;
}
private function find_media_in_job( stdClass $job ) {
$media = array();
foreach ( $job->elements as $element ) {
$field_type = explode( '_', $element->field_type );
if ( 'media' === $field_type[0] ) {
if ( ! isset( $media[ $field_type[1] ] ) ) {
$media[ $field_type[1] ] = array();
}
$media[ $field_type[1] ][ $field_type[2] ] = base64_decode( $element->field_data_translated );
}
}
return $media;
}
public function restore_placeholders_in_translated_job_body( $new_body ) {
/**
* Translation management is updating the translated job data with the post_content
* from the translated post.
* We want the translated job data to contain the placeholders so we need to
* find the captions we replaced and restore it with the version with the placeholders
*/
foreach ( $this->captions_map as $caption_with_placeholder => $caption_in_post ) {
$new_body = str_replace( $caption_in_post, $caption_with_placeholder, $new_body );
}
$this->captions_map = array();
return $new_body;
}
}

View File

@@ -0,0 +1,20 @@
<?php
class WPML_Media_Post_With_Media_Files_Factory {
/**
* @param $post_id
*
* @return WPML_Media_Post_With_Media_Files
*/
public function create( $post_id ) {
global $sitepress, $iclTranslationManagement;
return new WPML_Media_Post_With_Media_Files(
$post_id,
new WPML_Media_Img_Parse(),
new WPML_Media_Attachment_By_URL_Factory(),
$sitepress,
new WPML_Custom_Field_Setting_Factory( $iclTranslationManagement )
);
}
}

View File

@@ -0,0 +1,188 @@
<?php
class WPML_Media_Post_With_Media_Files {
/**
* @var int
*/
private $post_id;
/**
* @var WPML_Media_Img_Parse
*/
private $media_parser;
/**
* @var WPML_Media_Attachment_By_URL_Factory
*/
private $attachment_by_url_factory;
/**
* @var SitePress $sitepress
*/
private $sitepress;
/**
* @var WPML_Custom_Field_Setting_Factory
*/
private $cf_settings_factory;
/**
* WPML_Media_Post_With_Media_Files constructor.
*
* @param $post_id
* @param WPML_Media_Img_Parse $media_parser
* @param WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory
* @param SitePress $sitepress
* @param WPML_Custom_Field_Setting_Factory $cf_settings_factory
*/
public function __construct(
$post_id,
WPML_Media_Img_Parse $media_parser,
WPML_Media_Attachment_By_URL_Factory $attachment_by_url_factory,
SitePress $sitepress,
WPML_Custom_Field_Setting_Factory $cf_settings_factory
) {
$this->post_id = $post_id;
$this->media_parser = $media_parser;
$this->attachment_by_url_factory = $attachment_by_url_factory;
$this->sitepress = $sitepress;
$this->cf_settings_factory = $cf_settings_factory;
}
public function get_media_ids() {
$media_ids = array();
if ( $post = get_post( $this->post_id ) ) {
$content_to_parse = apply_filters( 'wpml_media_content_for_media_usage', $post->post_content, $post );
$post_content_media = $this->media_parser->get_imgs( $content_to_parse );
$media_ids = $this->_get_ids_from_media_array( $post_content_media );
if ( $featured_image = get_post_meta( $this->post_id, '_thumbnail_id', true ) ) {
$media_ids[] = $featured_image;
}
$media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
if ( $media_localization_settings['custom_fields'] ) {
$custom_fields_content = $this->get_content_in_translatable_custom_fields();
$custom_fields_media = $this->media_parser->get_imgs( $custom_fields_content );
$media_ids = array_merge( $media_ids, $this->_get_ids_from_media_array( $custom_fields_media ) );
}
if ( $gallery_media_ids = $this->get_gallery_media_ids( $content_to_parse ) ) {
$media_ids = array_unique( array_values( array_merge( $media_ids, $gallery_media_ids ) ) );
}
if ( $attached_media_ids = $this->get_attached_media_ids( $this->post_id ) ) {
$media_ids = array_unique( array_values( array_merge( $media_ids, $attached_media_ids ) ) );
}
}
return apply_filters( 'wpml_ids_of_media_used_in_post', $media_ids, $this->post_id );
}
/**
* @param array $media_array
*
* @return array
*/
private function _get_ids_from_media_array( $media_array ) {
$media_ids = array();
foreach ( $media_array as $media ) {
if ( isset( $media['attachment_id'] ) ) {
$media_ids[] = $media['attachment_id'];
} else {
$attachment_by_url = $this->attachment_by_url_factory->create( $media['attributes']['src'], wpml_get_current_language() );
if ( $attachment_id = $attachment_by_url->get_id() ) {
$media_ids[] = $attachment_id;
}
}
}
return $media_ids;
}
/**
* @param string $post_content
*
* @return array
*/
private function get_gallery_media_ids( $post_content ) {
$galleries_media_ids = array();
$gallery_shortcode_regex = '/\[gallery [^[]*ids=["\']([0-9,\s]+)["\'][^[]*\]/m';
if ( preg_match_all( $gallery_shortcode_regex, $post_content, $matches ) ) {
foreach ( $matches[1] as $gallery_ids_string ) {
$media_ids_array = explode( ',', $gallery_ids_string );
foreach ( $media_ids_array as $media_id ) {
if ( 'attachment' === get_post_type ( $media_id ) ) {
$galleries_media_ids[] = (int) $media_id;
}
}
}
}
return $galleries_media_ids;
}
/**
* @param $languages
*
* @return array
*/
public function get_untranslated_media( $languages ) {
$untranslated_media = array();
$post_media = $this->get_media_ids();
foreach ( $post_media as $attachment_id ) {
$post_element = new WPML_Post_Element( $attachment_id, $this->sitepress );
foreach ( $languages as $language ) {
$translation = $post_element->get_translation( $language );
if ( null === $translation || ! $this->media_file_is_translated( $attachment_id, $translation->get_id() ) ) {
$untranslated_media[] = $attachment_id;
break;
}
}
}
return $untranslated_media;
}
private function media_file_is_translated( $attachment_id, $translated_attachment_id ) {
return get_post_meta( $attachment_id, '_wp_attached_file', true )
!== get_post_meta( $translated_attachment_id, '_wp_attached_file', true );
}
private function get_content_in_translatable_custom_fields() {
$content = '';
$post_meta = get_metadata( 'post', $this->post_id );
foreach ( $post_meta as $meta_key => $meta_value ) {
$setting = $this->cf_settings_factory->post_meta_setting( $meta_key );
$is_translatable = $this->sitepress->get_wp_api()
->constant( 'WPML_TRANSLATE_CUSTOM_FIELD' ) === $setting->status();
if ( is_string( $meta_value[0] ) && $is_translatable ) {
$content .= $meta_value[0];
}
}
return $content;
}
private function get_attached_media_ids( $post_id ) {
$attachments = get_children(
array(
'post_parent' => $post_id,
'post_status' => 'inherit',
'post_type' => 'attachment',
)
);
return array_keys( $attachments );
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Class WPML_Media_Save_Translation_Factory
*/
class WPML_Media_Save_Translation_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $sitepress, $wpdb;
return new WPML_Media_Save_Translation( $sitepress, $wpdb, new WPML_Media_File_Factory(), new WPML_Translation_Element_Factory( $sitepress ) );
}
}

View File

@@ -0,0 +1,297 @@
<?php
class WPML_Media_Save_Translation implements IWPML_Action {
/**
* @var SitePress
*/
private $sitepress;
/**
* @var wpdb
*/
private $wpdb;
/**
* @var WPML_Media_File_Factory
*/
private $media_file_factory;
/**
* @var array
*/
private $post_data;
/**
* @var WPML_Translation_Element_Factory
*/
private $translation_element_factory;
/**
* WPML_Media_Save_Translation constructor.
*
* @param SitePress $sitepress
* @param wpdb $wpdb
* @param WPML_Media_File_Factory $media_file_factory
* @param WPML_Translation_Element_Factory $translation_element_factory
*/
public function __construct( SitePress $sitepress, wpdb $wpdb, WPML_Media_File_Factory $media_file_factory, WPML_Translation_Element_Factory $translation_element_factory ) {
$this->sitepress = $sitepress;
$this->wpdb = $wpdb;
$this->media_file_factory = $media_file_factory;
$this->translation_element_factory = $translation_element_factory;
}
public function add_hooks() {
add_action( 'wp_ajax_wpml_media_save_translation', array( $this, 'save_media_translation' ) );
}
public function save_media_translation() {
if ( wp_verify_nonce( $_POST['wpnonce'], 'media-translation' ) ) {
$post_array['ID'] = (int) $_POST['translated-attachment-id'];
$original_attachment_id = (int) $_POST['original-attachment-id'];
$translated_language = sanitize_text_field( $_POST['translated-language'] );
if ( isset( $_POST['translation']['title'] ) ) {
$post_array['post_title'] = sanitize_text_field( $_POST['translation']['title'] );
}
if ( isset( $_POST['translation']['caption'] ) ) {
$post_array['post_excerpt'] = sanitize_text_field( $_POST['translation']['caption'] );
}
if ( isset( $_POST['translation']['description'] ) ) {
$post_array['post_content'] = sanitize_text_field( $_POST['translation']['description'] );
}
if ( $post_array['ID'] ) {
$attachment_id = wp_update_post( $post_array );
if ( $this->should_restore_media() ) {
$this->restore_media_file( $attachment_id, $original_attachment_id, $translated_language );
}
} else {
$post_array['post_type'] = 'attachment';
$post_array['post_status'] = 'inherit';
$post_array['guid'] = get_post_field( 'guid', $original_attachment_id );
$post_array['post_mime_type'] = get_post_field( 'post_mime_type', $original_attachment_id );
$attachment_id = $this->create_attachment_translation( $original_attachment_id, $post_array );
$this->sitepress->set_element_language_details(
$attachment_id,
'post_attachment',
$this->get_post_trid_value(),
$this->get_post_lang_value()
);
if ( ! $this->has_media_upload() ) {
$this->copy_attached_file_info_from_original( $attachment_id, $original_attachment_id );
}
$this->mark_media_as_not_translated( $attachment_id, $translated_language );
}
$translation_status = WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION;
if ( $this->has_media_upload() ) {
$this->update_media_file( $attachment_id, $original_attachment_id, $translated_language );
$translation_status = WPML_Media_Translation_Status::TRANSLATED;
}
if ( isset( $_POST['translation']['alt-text'] ) ) {
update_post_meta(
$attachment_id,
'_wp_attachment_image_alt',
sanitize_text_field( $_POST['translation']['alt-text'] )
);
}
if ( 0 === strpos( get_post_field( 'post_mime_type', $original_attachment_id ), 'image/' ) ) {
$translated_thumb = wp_get_attachment_thumb_url( $attachment_id );
$original_thumb = wp_get_attachment_thumb_url( $original_attachment_id );
$media_file_is_translated = $translated_thumb !== $original_thumb;
} else {
$media_file_is_translated = get_attached_file( $attachment_id ) !== get_attached_file( $original_attachment_id );
$translated_thumb = wp_mime_type_icon( $original_attachment_id );
}
$media_usage = get_post_meta( $original_attachment_id, WPML_Media_Usage::FIELD_NAME, true );
$posts_list = array();
if ( isset( $media_usage['posts'] ) ) {
foreach ( $media_usage['posts'] as $post_id ) {
$post_element = $this->translation_element_factory->create( $post_id, 'post' );
$post_translation = $post_element->get_translation( $translated_language );
if ( $post_translation ) {
$posts_list[] = array(
'url' => get_edit_post_link( $post_translation->get_id() ),
'title' => get_post_field( 'post_title', $post_translation->get_id() ),
);
}
}
}
if ( isset( $media_usage['posts'] ) && $this->should_restore_media() ) {
do_action( 'wpml_restored_media_file_translation', $attachment_id, $media_usage['posts'] );
}
$response = array(
'attachment_id' => $attachment_id,
'thumb' => $media_file_is_translated ? $translated_thumb : false,
'status' => $translation_status,
'usage' => $posts_list,
);
wp_send_json_success( $response );
} else {
wp_send_json_error( array( 'error' => __( 'Invalid nonce', 'wpml-media' ) ) );
}
}
private function has_media_upload() {
return ! empty( $_POST['update-media-file'] );
}
private function should_restore_media() {
return ! empty( $_POST['restore-media'] );
}
/**
* @param int $original_attachment_id
* @param array $post_array
*
* @return int
*/
private function create_attachment_translation( $original_attachment_id, $post_array ) {
$post_element = $this->translation_element_factory->create( $original_attachment_id, 'post' );
$this->set_post_trid_value( $post_element->get_trid() );
$this->set_post_lang_value( $_POST['translated-language'] );
add_filter( 'wpml_tm_save_post_trid_value', array( $this, 'get_post_trid_value' ) );
add_filter( 'wpml_tm_save_post_lang_value', array( $this, 'get_post_lang_value' ) );
$attachment_id = wp_insert_post( $post_array );
remove_filter( 'wpml_tm_save_post_trid_value', array( $this, 'get_post_trid_value' ) );
remove_filter( 'wpml_tm_save_post_lang_value', array( $this, 'get_post_lang_value' ) );
return $attachment_id;
}
private function set_post_trid_value( $value ) {
$this->post_data['post_icl_trid'] = $value;
}
public function get_post_trid_value() {
return $this->post_data['post_icl_trid'];
}
private function set_post_lang_value( $value ) {
$this->post_data['post_icl_language'] = $value;
}
public function get_post_lang_value() {
return $this->post_data['post_icl_language'];
}
private function restore_media_file( $attachment_id, $original_attachment_id, $language ) {
$media_file = $this->media_file_factory->create( $attachment_id );
$media_file->delete();
$this->copy_attached_file_info_from_original( $attachment_id, $original_attachment_id );
$this->mark_media_as_not_translated( $original_attachment_id, $language );
}
private function copy_attached_file_info_from_original( $attachment_id, $original_attachment_id ) {
$meta_keys = array(
'_wp_attachment_metadata',
'_wp_attached_file',
'_wp_attachment_backup_sizes',
);
foreach ( $meta_keys as $meta_key ) {
update_post_meta(
$attachment_id,
$meta_key,
get_post_meta( $original_attachment_id, $meta_key, true )
);
}
/**
* Fires after attachment post meta is copied
*
* @since 4.1.0
*
* @param int $original_attachment_id The ID of the source/original attachment.
* @param int $attachment_id The ID of the duplicated attachment.
*/
do_action( 'wpml_after_copy_attached_file_postmeta', $original_attachment_id, $attachment_id );
}
private function mark_media_as_not_translated( $attachment_id, $language ) {
update_post_meta( $attachment_id, WPML_Media_Translation_Status::STATUS_PREFIX . $language, WPML_Media_Translation_Status::NEEDS_MEDIA_TRANSLATION );
}
private function mark_media_as_translated( $attachment_id, $language ) {
update_post_meta( $attachment_id, WPML_Media_Translation_Status::STATUS_PREFIX . $language, WPML_Media_Translation_Status::TRANSLATED );
}
/**
* @param array $file
*
* @return array
*/
private function get_attachment_post_data( $file ) {
$postarr = array(
'post_mime_type' => $file['type'],
'guid' => $file['url'],
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql', 1 ),
);
return $postarr;
}
private function update_media_file( $attachment_id, $original_attachment_id, $translated_language_code ) {
$transient_key = WPML_Media_Attachment_Image_Update::TRANSIENT_FILE_UPLOAD_PREFIX . $original_attachment_id . '_' . $translated_language_code;
$media_file_upload = get_transient( $transient_key );
if ( $media_file_upload ) {
$file = $media_file_upload['upload'];
// delete previous media file + sizes
$media_file = $this->media_file_factory->create( $attachment_id );
$media_file->delete();
$post_data = $this->get_attachment_post_data( $file );
$this->wpdb->update( $this->wpdb->posts, $post_data, array( 'ID' => $attachment_id ) );
update_attached_file( $attachment_id, $file['file'] );
/**
* Fires after attached file is updated
*
* @since 4.1.0
*
* @param int $attachment_id The ID of uploaded attachment.
* @param array $file {
* Uploaded file data.
*
* @type string file Absolute path to file.
*
* @type string url File URL.
*
* @type string type File type.
* }
* @param string $translated_language_code Attachment language code.
*/
do_action( 'wpml_updated_attached_file', $attachment_id, $file, $translated_language_code );
wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file['file'] ) );
$this->mark_media_as_translated( $original_attachment_id, $translated_language_code );
do_action( 'wpml_added_media_file_translation', $original_attachment_id, $file, $translated_language_code );
delete_transient( $transient_key );
}
}
}

View File

@@ -0,0 +1,18 @@
<?php
class WPML_Media_Set_Posts_Media_Flag_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $wpdb;
$post_media_usage_factory = new WPML_Media_Post_Media_Usage_Factory();
return new WPML_Media_Set_Posts_Media_Flag(
$wpdb,
wpml_get_admin_notices(),
$post_media_usage_factory->create(),
new WPML_Media_Post_With_Media_Files_Factory()
);
}
}

View File

@@ -0,0 +1,150 @@
<?php
class WPML_Media_Set_Posts_Media_Flag implements IWPML_Action {
const HAS_MEDIA_POST_FLAG = '_wpml_media_has_media';
const BATCH_SIZE = 100;
/**
* @var wpdb $wpdb
*/
private $wpdb;
/**
* @var WPML_Notices
*/
private $wpml_notices;
/**
* @var WPML_Media_Post_Media_Usage
*/
private $post_media_usage;
/**
* @var WPML_Media_Post_With_Media_Files_Factory
*/
private $post_with_media_files_factory;
public function __construct(
wpdb $wpdb,
WPML_Notices $wpml_notices,
WPML_Media_Post_Media_Usage $post_media_usage,
WPML_Media_Post_With_Media_Files_Factory $post_with_media_files_factory
) {
$this->wpdb = $wpdb;
$this->wpml_notices = $wpml_notices;
$this->post_media_usage = $post_media_usage;
$this->post_with_media_files_factory = $post_with_media_files_factory;
}
public function add_hooks() {
add_action( 'wp_ajax_' . WPML_Media_Posts_Media_Flag_Notice::PREPARE_ACTION, array( $this, 'clear_flags' ) );
add_action( 'wp_ajax_' . WPML_Media_Posts_Media_Flag_Notice::PROCESS_ACTION, array( $this, 'process_batch' ) );
add_action( 'save_post', array( $this, 'update_post_flag' ) );
}
public function clear_flags() {
if ( $this->verify_nonce( WPML_Media_Posts_Media_Flag_Notice::PREPARE_ACTION ) ) {
if ( ! WPML_Media::has_setup_started() ) {
$this->wpdb->delete( $this->wpdb->postmeta, array( 'meta_key' => self::HAS_MEDIA_POST_FLAG ), array( '%s' ) );
}
wp_send_json_success( array( 'status' => __( 'Running setup...', 'wpml-media' ) ) );
} else {
wp_send_json_error( array( 'status' => 'Invalid nonce' ) );
}
}
public function process_batch() {
if ( $this->verify_nonce( WPML_Media_Posts_Media_Flag_Notice::PROCESS_ACTION ) ) {
$this->mark_started();
$continue = false;
$status = __( 'Setup complete!', 'wpml-media' );
$offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0;
if ( ! WPML_Media::has_setup_run() ) {
$sql = $this->wpdb->prepare( "
SELECT SQL_CALC_FOUND_ROWS ID, post_content FROM {$this->wpdb->posts} p
JOIN {$this->wpdb->prefix}icl_translations t
ON t.element_id = p.ID AND t.element_type LIKE 'post_%'
LEFT JOIN {$this->wpdb->prefix}postmeta m ON p.ID = m.post_id AND m.meta_key='%s'
WHERE p.post_type NOT IN ( 'auto-draft', 'attachment', 'revision' )
AND t.source_language_code IS NULL AND m.meta_id IS NULL
ORDER BY ID ASC
LIMIT %d, %d
", self::HAS_MEDIA_POST_FLAG, $offset, self::BATCH_SIZE );
$posts = $this->wpdb->get_results( $sql );
$total_posts_found = $this->wpdb->get_var( 'SELECT FOUND_ROWS()' );
if ( $continue = ( count( $posts ) > 0 ) ) {
$this->flag_posts( $posts );
$this->record_media_usage( $posts );
$progress = round( 100*min( $offset, $total_posts_found )/$total_posts_found );
$status = sprintf( __( 'Setup in progress: %d%% complete...', 'wpml-media' ), $progress );
}
}
if ( ! $continue ) {
$this->mark_complete();
}
wp_send_json_success( array(
'status' => $status,
'offset' => $offset + self::BATCH_SIZE,
'continue' => $continue
) );
} else {
wp_send_json_error( array( 'status' => 'Invalid nonce' ) );
}
}
private function verify_nonce( $action ) {
return isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], $action );
}
/**
* @param array $posts
*/
private function flag_posts( $posts ) {
foreach ( $posts as $post ) {
$this->update_post_flag( $post->ID );
}
}
public function update_post_flag( $post_id ) {
$post_with_media_files = $this->post_with_media_files_factory->create( $post_id );
if ( $post_with_media_files->get_media_ids() ) {
update_post_meta( $post_id, self::HAS_MEDIA_POST_FLAG, 1 );
} else {
delete_post_meta( $post_id, self::HAS_MEDIA_POST_FLAG );
}
}
/**
* @param array $posts
*/
private function record_media_usage( $posts ) {
foreach ( $posts as $post ) {
$this->post_media_usage->update_media_usage( $post->ID );
}
}
private function mark_complete() {
WPML_Media::set_setup_run();
$this->wpml_notices->remove_notice(
WPML_Media_Posts_Media_Flag_Notice::NOTICE_GROUP,
WPML_Media_Posts_Media_Flag_Notice::NOTICE_ID
);
}
private function mark_started() {
WPML_Media::set_setup_started();
}
}

View File

@@ -0,0 +1,138 @@
<?php
/**
* @author OnTheGo Systems
*/
class WPML_Media_Sizes {
/**
* @param array $img
*
* @return null|string
*/
public function get_size_from_class( array $img ) {
if ( array_key_exists( 'attributes', $img ) && array_key_exists( 'class', $img['attributes'] ) ) {
$classes = explode( ' ', $img['attributes']['class'] );
foreach ( $classes as $class ) {
if ( strpos( $class, 'size-' ) === 0 ) {
$class_parts = explode( '-', $class );
if ( count( $class_parts ) >= 2 ) {
unset( $class_parts[0] );
return implode( '-', $class_parts );
}
}
}
}
return null;
}
/**
* @param array $img
*
* @return null|string
*/
public function get_size_from_attributes( array $img ) {
if (
array_key_exists( 'attributes', $img )
&& array_key_exists( 'width', $img['attributes'] )
&& array_key_exists( 'height', $img['attributes'] )
) {
$width = $img['attributes']['width'];
$height = $img['attributes']['height'];
$size_name = $this->get_image_size_name( $width, $height );
if ( $size_name ) {
return $size_name;
}
}
return null;
}
/**
* @param array $img
*
* @return null|string
*/
public function get_attachment_size( array $img ) {
$size = null;
if ( array_key_exists( 'size', $img ) ) {
$size = $img['size'];
}
if ( ! $size ) {
$size = $this->get_size_from_class( $img );
}
if ( ! $size ) {
$size = $this->get_size_from_attributes( $img );
}
if ( ! $size ) {
$size = $this->get_size_from_url( $img );
}
return $size;
}
/**
* @param string $width
* @param string $height
*
* @return null|string
*/
private function get_image_size_name( $width, $height ) {
global $_wp_additional_image_sizes;
foreach ( get_intermediate_image_sizes() as $size ) {
if ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
if ( $width == $_wp_additional_image_sizes[ $size ]['width'] && $height == $_wp_additional_image_sizes[ $size ]['height'] ) {
return $size;
}
} elseif ( in_array( $size, array( 'thumbnail', 'medium', 'medium_large', 'large' ) ) ) {
if ( $width == get_option( "{$size}_size_w" ) && $height == get_option( "{$size}_size_h" ) ) {
return $size;
}
}
}
return null;
}
/**
* @param array $img
*
* @return null|string
*/
private function get_size_from_url( array $img ) {
$size = null;
if ( isset( $img['attributes']['src'], $img['attachment_id'] ) ) {
$size = $this->get_image_size_from_url( $img['attributes']['src'], $img['attachment_id'] );
}
return $size;
}
/**
* @param $url
* @param $attachment_id
*
* @return null|string
*/
public function get_image_size_from_url( $url, $attachment_id ) {
$size = null;
$thumb_file_name = basename( $url );
$attachment_meta_data = wp_get_attachment_metadata( $attachment_id );
foreach ( $attachment_meta_data['sizes'] as $key => $size_array ) {
if ( $thumb_file_name === $size_array['file'] ) {
$size = $key;
break;
}
}
return $size;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Class WPML_Media_String_Images_Translation_Factory
*/
class WPML_Media_String_Images_Translation_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $wpdb, $sitepress;
$media_localization_settings = WPML_Media::get_setting( 'media_files_localization' );
if ( $media_localization_settings['strings'] ) {
$image_translator = new WPML_Media_Image_Translate( $sitepress, new WPML_Media_Attachment_By_URL_Factory() );
$image_updater = new WPML_Media_Translated_Images_Update( new WPML_Media_Img_Parse(), $image_translator, new WPML_Media_Sizes() );
$string_factory = new WPML_ST_String_Factory( $wpdb );
return new WPML_Media_String_Images_Translation( $image_updater, $string_factory );
}
return null;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Class WPML_Media_String_Images_Translation
* Translate images in posts strings translations when a string translation is created or updated
*/
class WPML_Media_String_Images_Translation implements IWPML_Action {
/**
* @var WPML_Media_String_Images_Translation
*/
private $images_updater;
/**
* @var WPML_ST_String_Factory
*/
private $string_factory;
/**
* WPML_Media_String_Images_Translation constructor.
*
* @param WPML_Media_Translated_Images_Update $images_updater
* @param WPML_ST_String_Factory $string_factory
*/
public function __construct( WPML_Media_Translated_Images_Update $images_updater, WPML_ST_String_Factory $string_factory ) {
$this->images_updater = $images_updater;
$this->string_factory = $string_factory;
}
public function add_hooks() {
add_filter( 'wpml_st_string_translation_before_save', array( $this, 'translate_images' ), PHP_INT_MAX, 3 );
}
/**
* @param array $translation_data
* @param string $target_language
* @param int $string_id
*
* @return array
*/
public function translate_images( $translation_data, $target_language, $string_id ) {
if ( ! empty( $translation_data['value'] ) ) {
$original_string = $this->string_factory->find_by_id( $string_id );
$translation_data['value'] = $this->images_updater->replace_images_with_translations(
$translation_data['value'],
$target_language,
$original_string->get_language()
);
}
return $translation_data;
}
}

View File

@@ -0,0 +1,135 @@
<?php
/**
* Class WPML_Media_Translated_Images_Update
* Translates images in a given text
*/
class WPML_Media_Translated_Images_Update {
/**
* @var WPML_Media_Img_Parse
*/
private $img_parser;
/**
* @var WPML_Media_Image_Translate
*/
private $image_translator;
/**
* @var WPML_Media_Sizes
*/
private $media_sizes;
/**
* WPML_Media_Translated_Images_Update constructor.
*
* @param WPML_Media_Img_Parse $img_parser
* @param WPML_Media_Image_Translate $image_translator
* @param WPML_Media_Sizes $media_sizes
*/
public function __construct( WPML_Media_Img_Parse $img_parser, WPML_Media_Image_Translate $image_translator, WPML_Media_Sizes $media_sizes ) {
$this->img_parser = $img_parser;
$this->image_translator = $image_translator;
$this->media_sizes = $media_sizes;
}
/**
* @param string $text
* @param string $source_language
* @param string $target_language
*
* @return string
*/
public function replace_images_with_translations( $text, $target_language, $source_language = null ) {
$imgs = $this->img_parser->get_imgs( $text );
foreach ( $imgs as $img ) {
$attachment_id = $translated_id = false;
if ( isset( $img['attachment_id'] ) && $img['attachment_id'] ) {
$attachment_id = $img['attachment_id'];
$translated_id = apply_filters(
'wpml_object_id',
$attachment_id,
'attachment',
true,
$target_language
);
$size = $this->media_sizes->get_attachment_size( $img );
$translated_src = $this->image_translator->get_translated_image( $attachment_id, $target_language, $size );
} else {
$translated_src = $this->get_translated_image_by_url( $target_language, $source_language, $img );
}
if ( $translated_src && $translated_src !== $img['attributes']['src'] ) {
$text = $this->replace_image_src( $text, $img['attributes']['src'], $translated_src );
}
if ( $attachment_id && $attachment_id !== $translated_id ) {
$text = $this->replace_att_class( $text, $attachment_id, $translated_id );
$text = $this->replace_att_in_block( $text, $attachment_id, $translated_id );
}
}
return $text;
}
/**
* @param string $text
* @param string $from
* @param string $to
*
* @return string
*/
private function replace_image_src( $text, $from, $to ) {
return str_replace( $from, $to, $text );
}
/**
* @param string $text
* @param string $from
* @param string $to
*
* @return string
*/
private function replace_att_class( $text, $from, $to ) {
$pattern = '/\bwp-image-' . $from . '\b/u';
$replacement = 'wp-image-' . $to;
return preg_replace( $pattern, $replacement, $text );
}
/**
* @param string $text
* @param string $from
* @param string $to
*
* @return string
*/
private function replace_att_in_block( $text, $from, $to ) {
$pattern = '/<!-- wp:image {.*?"id":(' . $from . '),.*?-->/u';
$replacement = function ( $matches ) use ( $to ) {
return str_replace( '"id":' . $matches[1], '"id":' . $to, $matches[0] );
};
return preg_replace_callback( $pattern, $replacement, $text );
}
/**
* @param $target_language
* @param $source_language
* @param $img
*
* @return bool|string
*/
private function get_translated_image_by_url( $target_language, $source_language, $img ) {
if ( null === $source_language ) {
$source_language = wpml_get_current_language();
}
$translated_src = $this->image_translator->get_translated_image_by_url(
$img['attributes']['src'],
$source_language,
$target_language
);
return $translated_src;
}
}

View File

@@ -0,0 +1,11 @@
<?php
class WPML_Media_Translation_Status_Factory implements IWPML_Backend_Action_Loader, IWPML_REST_Action_Loader {
public function create() {
global $sitepress;
return new WPML_Media_Translation_Status( $sitepress );
}
}

View File

@@ -0,0 +1,172 @@
<?php
class WPML_Media_Translation_Status implements IWPML_Action {
const NOT_TRANSLATED = 'media-not-translated';
const IN_PROGRESS = 'in-progress';
const TRANSLATED = 'media-translated';
const NEEDS_MEDIA_TRANSLATION = 'needs-media-translation';
const STATUS_PREFIX = '_translation_status_';
/**
* @var SitePress
*/
private $sitepress;
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
public function add_hooks() {
add_action( 'wpml_tm_send_post_jobs', array( $this, 'set_translation_status_in_progress' ) );
add_action( 'wpml_pro_translation_completed', array( $this, 'save_bundled_media_translation' ), 10, 3 );
}
public function set_translation_status_in_progress( WPML_TM_Translation_Batch $batch ) {
foreach ( $batch->get_elements() as $item ) {
foreach ( $item->get_media_to_translations() as $attachment_id ) {
foreach ( array_keys( $item->get_target_langs() ) as $lang ) {
$this->set_status( $attachment_id, $lang, self::IN_PROGRESS );
}
}
}
}
private function set_status( $attachment_id, $language, $status ) {
update_post_meta( $attachment_id, self::STATUS_PREFIX . $language, $status );
}
public function save_bundled_media_translation( $new_post_id, $fields, $job ) {
$media_translations = $this->get_media_translations( $job );
$translation_package = new WPML_Element_Translation_Package();
foreach ( $media_translations as $attachment_id => $translation_data ) {
$attachment_translation_id = $this->save_attachment_translation(
$attachment_id,
$translation_data,
$translation_package,
$job->language_code
);
if ( $this->should_translate_media_image( $job, $attachment_id ) ) {
$this->set_status( $attachment_id, $job->language_code, self::NEEDS_MEDIA_TRANSLATION );
}
}
}
private function should_translate_media_image( $job, $attachment_id ) {
foreach ( $job->elements as $element ) {
if ( 'should_translate_media_image_' . $attachment_id === $element->field_type && $element->field_data ) {
return true;
}
}
return false;
}
private function get_media_translations( $job ) {
$media = array();
$media_field_regexp = '#^media_([0-9]+)_([a-z_]+)$#';
foreach ( $job->elements as $element ) {
if ( preg_match( $media_field_regexp, $element->field_type, $matches ) ) {
list( , $attachment_id, $media_field ) = $matches;
$media[ $attachment_id ][ $media_field ] = $element;
}
}
return $media;
}
/**
* @param int $attachment_id
* @param array $translation_data
* @param WPML_Element_Translation_Package $translation_package
* @param string $language
* @return bool|int|WP_Error
*/
private function save_attachment_translation( $attachment_id, $translation_data, $translation_package, $language ) {
$postarr = array();
$alt_text = null;
foreach ( $translation_data as $field => $data ) {
$translated_value = $translation_package->decode_field_data(
$data->field_data_translated,
$data->field_format
);
if ( 'alt_text' === $field ) {
$alt_text = $translated_value;
} else {
switch ( $field ) {
case 'title':
$wp_post_field = 'post_title';
break;
case 'caption':
$wp_post_field = 'post_excerpt';
break;
case 'description':
$wp_post_field = 'post_content';
break;
default:
$wp_post_field = '';
}
if ( $wp_post_field ) {
$postarr[ $wp_post_field ] = $translated_value;
}
}
}
$post_element = new WPML_Post_Element( $attachment_id, $this->sitepress );
$attachment_translation = $post_element->get_translation( $language );
$attachment_translation_id = null !== $attachment_translation ? $attachment_translation->get_id() : false;
if ( $attachment_translation_id ) {
$postarr['ID'] = $attachment_translation_id;
wp_update_post( $postarr );
} else {
$postarr['post_type'] = 'attachment';
$postarr['post_status'] = 'inherit';
$postarr['guid'] = get_post_field( 'guid', $attachment_id );
$postarr['post_mime_type'] = get_post_field( 'post_mime_type', $attachment_id );
$attachment_translation_id = wp_insert_post( $postarr );
$this->sitepress->set_element_language_details( $attachment_translation_id, 'post_attachment', $post_element->get_trid(), $language );
$this->copy_attached_file_info_from_original( $attachment_translation_id, $attachment_id );
}
if ( null !== $alt_text ) {
update_post_meta( $attachment_translation_id, '_wp_attachment_image_alt', $alt_text );
}
return $attachment_translation_id;
}
private function copy_attached_file_info_from_original( $attachment_id, $original_attachment_id ) {
$meta_keys = array(
'_wp_attachment_metadata',
'_wp_attached_file',
'_wp_attachment_backup_sizes',
);
foreach ( $meta_keys as $meta_key ) {
update_post_meta(
$attachment_id,
$meta_key,
get_post_meta( $original_attachment_id, $meta_key, true )
);
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
class WPML_Media_Populate_Media_Strings_Translations_Factory implements IWPML_Backend_Action_Loader {
public function create() {
global $sitepress;
if ( class_exists( 'WPML_Element_Translation_Package' ) ) {
return new WPML_Media_Populate_Media_Strings_Translations(
new WPML_Translation_Element_Factory( $sitepress ),
new WPML_Element_Translation_Package()
);
}
}
}

View File

@@ -0,0 +1,104 @@
<?php
class WPML_Media_Populate_Media_Strings_Translations implements IWPML_Action {
/**
* @var WPML_Translation_Element_Factory
*/
private $translation_element_factory;
/**
* @var WPML_Element_Translation_Package
*/
private $translation_package;
public function __construct(
WPML_Translation_Element_Factory $translation_element_factory,
WPML_Element_Translation_Package $translation_package
) {
$this->translation_element_factory = $translation_element_factory;
$this->translation_package = $translation_package;
}
public function add_hooks() {
add_filter( 'wpml_tm_populate_prev_translation', array( $this, 'populate' ), 10, 3 );
}
public function populate( $prev_translation, $package, $lang ) {
if ( ! $prev_translation ) {
foreach ( $package['contents'] as $field => $data ) {
if ( $media_field = $this->is_media_field( $field ) ) {
$attachment = $this->translation_element_factory->create( $media_field['id'], 'post' );
$attachment_translation = $attachment->get_translation( $lang );
if ( $attachment_translation ) {
$original_id = (int) $media_field['id'];
$translation_id = $attachment_translation->get_id();
switch ( $media_field['field'] ) {
case 'title':
$translated_value = $this->get_post_field( 'post_title', $original_id, $translation_id );
break;
case 'caption':
$translated_value = $this->get_post_field( 'post_excerpt', $original_id, $translation_id );
break;
case 'description':
$translated_value = $this->get_post_field( 'post_content', $original_id, $translation_id );
break;
case 'alt_text':
$translated_value = get_post_meta( $translation_id, '_wp_attachment_image_alt', true );
if ( ! $translated_value ) {
$translated_value = get_post_meta( $original_id, '_wp_attachment_image_alt', true );
}
break;
default:
$translated_value = false;
}
if ( $translated_value ) {
$prev_translation[ $field ] = wpml_tm_create_translated_field(
'', $this->translation_package->encode_field_data( $translated_value ), true
);
}
}
}
}
}
return $prev_translation;
}
private function is_media_field( $field ) {
$media_field = array();
if ( preg_match( '#^media_([0-9]+)_([a-z_]+)$#', $field, $matches ) ) {
$media_field['id'] = $matches[1];
$media_field['field'] = $matches[2];
}
return $media_field;
}
/**
* @param string $field
* @param int $original_id
* @param int $translation_id
*
* @return string
*/
private function get_post_field( $field, $original_id, $translation_id ) {
$value = get_post_field( $field, $translation_id );
if ( ! $value ) {
$value = get_post_field( $field, $original_id );
}
return $value;
}
}