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,290 @@
<?php
use WPML\API\Sanitize;
/**
* Class WPML_Admin_Post_Actions
*
* @package wpml-core
* @subpackage post-translation
*/
class WPML_Admin_Post_Actions extends WPML_Post_Translation {
const DUPLICATE_MEDIA_META_KEY = '_wpml_media_duplicate';
const DUPLICATE_FEATURED_META_KEY = '_wpml_media_featured';
const DUPLICATE_MEDIA_GLOBAL_KEY = 'duplicate_media';
const DUPLICATE_FEATURED_GLOBAL_KEY = 'duplicate_media';
private $http_referer;
public function init() {
parent::init ();
if ( $this->is_setup_complete() ) {
add_action ( 'delete_post', array( $this, 'delete_post_actions' ) );
add_action ( 'wp_trash_post', array( $this, 'trashed_post_actions' ) );
add_action ( 'untrashed_post', array( $this, 'untrashed_post_actions' ) );
}
}
/**
* @param int $post_id
* @param string $post_status
*
* @return null|int
*/
function get_save_post_trid( $post_id, $post_status ) {
$trid = $this->get_element_trid( $post_id );
if ( ! ( $this->is_inner_post_insertion() && $this->is_editing_different_post( $post_id ) ) ) {
$trid = $trid ? $trid : filter_var( isset( $_POST['icl_trid'] ) ? $_POST['icl_trid'] : '', FILTER_SANITIZE_NUMBER_INT );
$trid = $trid ? $trid : filter_var( isset( $_GET['trid'] ) ? $_GET['trid'] : '', FILTER_SANITIZE_NUMBER_INT );
$trid = $trid ? $trid : $this->get_trid_from_referer();
}
$trid = apply_filters( 'wpml_save_post_trid_value', $trid, $post_status );
return $trid;
}
/**
* @param int $post_id
* @param WP_Post $post
*/
public function save_post_actions( $post_id, $post ) {
global $sitepress;
$this->defer_term_counting();
if ( ! $post ) {
$post = get_post( $post_id );
}
// exceptions
$http_referer = $this->get_http_referer();
if ( ! $this->has_save_post_action( $post ) && ! $http_referer->is_rest_request_called_from_post_edit_page() ) {
return;
}
if ( WPML_WordPress_Actions::is_bulk_trash( $post_id ) ||
WPML_WordPress_Actions::is_bulk_untrash( $post_id ) ||
$this->has_invalid_language_details_on_heartbeat()
) {
return;
}
$default_language = $sitepress->get_default_language();
$post_vars = $this->get_post_vars( $post );
if ( isset( $post_vars['action'] ) && $post_vars['action'] === 'post-quickpress-publish' ) {
$language_code = $default_language;
} else {
if( isset( $post_vars['post_ID'] ) ){
$post_id = $post_vars['post_ID'];
}
$language_code = $this->get_save_post_lang( $post_id, $sitepress );
}
if ( $this->is_inline_action( $post_vars ) && ! ( $language_code = $this->get_element_lang_code(
$post_id
) )
) {
return;
}
if ( isset( $post_vars['icl_translation_of'] ) && is_numeric( $post_vars['icl_translation_of'] ) ) {
$translation_of_data_prepared = $this->wpdb->prepare(
"SELECT trid, language_code
FROM {$this->wpdb->prefix}icl_translations
WHERE element_id=%d
AND element_type=%s
LIMIT 1",
$post_vars['icl_translation_of'],
'post_' . $post->post_type
);
list( $trid, $source_language ) = $this->wpdb->get_row( $translation_of_data_prepared, 'ARRAY_N' );
}
if ( isset( $post_vars['icl_translation_of'] ) && $post_vars['icl_translation_of'] == 'none' ) {
$trid = null;
$source_language = $language_code;
} else {
$trid = isset( $trid ) && $trid ? $trid : $this->get_save_post_trid( $post_id, $post->post_status );
// after getting the right trid set the source language from it by referring to the root translation
// of this trid, in case no proper source language has been set yet
$source_language = isset( $source_language )
? $source_language : $this->get_save_post_source_lang( $trid, $language_code, $default_language );
}
if ( isset( $post_vars['icl_tn_note'] ) ) {
update_post_meta( $post_id, '_icl_translator_note', $post_vars['icl_tn_note'] );
}
$this->after_save_post( $trid, $post_vars, $language_code, $source_language );
if ( 'attachment' !== $post->post_type ) {
$this->save_media_options( $post_id, $source_language );
}
}
/**
* @param int $post_id
* @param string|null $source_language
*/
private function save_media_options( $post_id, $source_language ) {
if ( $this->has_post_media_options_metabox() ) {
$original_post_id = isset( $_POST['icl_translation_of'] )
? filter_var( $_POST['icl_translation_of'], FILTER_SANITIZE_NUMBER_INT ) : $post_id;
$duplicate_media = isset( $_POST['wpml_duplicate_media'] )
? filter_var( $_POST['wpml_duplicate_media'], FILTER_SANITIZE_NUMBER_INT ) : false;
$duplicate_featured = isset( $_POST['wpml_duplicate_featured'] )
? filter_var( $_POST['wpml_duplicate_featured'], FILTER_SANITIZE_NUMBER_INT ) : false;
update_post_meta( $original_post_id, self::DUPLICATE_MEDIA_META_KEY, (int) $duplicate_media );
update_post_meta( $original_post_id, self::DUPLICATE_FEATURED_META_KEY, (int) $duplicate_featured );
} else {
$this->sync_media_options_with_original_or_global_settings( $post_id, $source_language );
}
}
private function has_post_media_options_metabox() {
return array_key_exists( WPML_Meta_Boxes_Post_Edit_HTML::FLAG_HAS_MEDIA_OPTIONS, $_POST );
}
/**
* @param int $post_id
* @param string|null $source_language
*/
private function sync_media_options_with_original_or_global_settings( $post_id, $source_language ) {
global $sitepress;
$source_post_id = $sitepress->get_object_id( $post_id, get_post_type( $post_id ), false, $source_language );
$is_translation = $source_post_id && $source_post_id !== $post_id;
foreach (
array(
self::DUPLICATE_FEATURED_META_KEY => self::DUPLICATE_FEATURED_GLOBAL_KEY,
self::DUPLICATE_MEDIA_META_KEY => self::DUPLICATE_MEDIA_GLOBAL_KEY,
) as $meta_key => $global_key
) {
$source_value = get_post_meta( $source_post_id, $meta_key, true );
if ( '' === $source_value ) {
// fallback to global setting
$media_options = get_option( '_wpml_media', array() );
if ( isset( $media_options['new_content_settings'][ $global_key ] ) ) {
$source_value = (int) $media_options['new_content_settings'][ $global_key ];
if ( $source_post_id ) {
update_post_meta( $source_post_id, $meta_key, $source_value );
}
}
}
if ( '' !== $source_value && $is_translation ) {
update_post_meta( $post_id, $meta_key, $source_value );
}
}
}
private function has_invalid_language_details_on_heartbeat() {
if ( ! WPML_WordPress_Actions::is_heartbeat() ) {
return false;
}
if ( isset( $_POST['data']['icl_post_language'], $_POST['data']['icl_trid'] ) ) {
$_POST['icl_post_language'] = Sanitize::string( $_POST['data']['icl_post_language'] );
$_POST['icl_trid'] = filter_var( $_POST['data']['icl_trid'], FILTER_SANITIZE_NUMBER_INT );
return false;
}
return true;
}
/**
* @param integer $post_id
* @param SitePress $sitepress
*
* @return null|string
*/
public function get_save_post_lang( $post_id, $sitepress ) {
$language_code = null;
if ( isset( $_POST['post_ID'] ) && (int) $_POST['post_ID'] === (int) $post_id ) {
$language_code = filter_var(
( isset( $_POST['icl_post_language'] ) ? $_POST['icl_post_language'] : '' ),
FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
$language_code = $language_code
? $language_code
: filter_input(
INPUT_GET,
'lang',
FILTER_SANITIZE_FULL_SPECIAL_CHARS
);
return $language_code ? $language_code : parent::get_save_post_lang( $post_id, $sitepress );
}
/**
* @param array $post_vars
* @return bool
*/
private function is_inline_action( $post_vars ) {
return isset( $post_vars[ 'action' ] )
&& $post_vars[ 'action' ] == 'inline-save'
|| isset( $_GET[ 'bulk_edit' ] )
|| isset( $_GET[ 'doing_wp_cron' ] )
|| ( isset( $_GET[ 'action' ] )
&& $_GET[ 'action' ] == 'untrash' );
}
/**
* @param int $trid
* @param string $language_code
* @param string $default_language
*
* @return null|string
*/
protected function get_save_post_source_lang( $trid, $language_code, $default_language ) {
$source_language = filter_input ( INPUT_GET, 'source_lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$source_language = $source_language ? $source_language : $this->get_source_language_from_referer();
$source_language = $source_language ? $source_language : SitePress::get_source_language_by_trid ( $trid );
$source_language = $source_language === 'all' ? $default_language : $source_language;
$source_language = $source_language !== $language_code ? $source_language : null;
return $source_language;
}
/**
* Gets the source_language $_GET parameter from the HTTP_REFERER
*
* @return string|bool
*/
private function get_source_language_from_referer() {
if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) {
return false;
}
$referer = $_SERVER['HTTP_REFERER'];
$query = (string) wpml_parse_url( $referer, PHP_URL_QUERY );
parse_str( $query, $query_parts );
return isset( $query_parts['source_lang'] ) ? $query_parts['source_lang'] : false;
}
public function get_trid_from_referer() {
$http_referer = $this->get_http_referer();
return $http_referer->get_trid();
}
protected function get_http_referer() {
if ( ! $this->http_referer ) {
$factory = new WPML_URL_HTTP_Referer_Factory();
$this->http_referer = $factory->create();
}
return $this->http_referer;
}
}

View File

@@ -0,0 +1,103 @@
<?php
class WPML_Comment_Duplication{
public function move_to_original($duplicate_of, $post_duplicates, $comment){
global $wpml_post_translations, $wpdb;
$_orig_lang = $wpml_post_translations->get_element_lang_code ( $duplicate_of );
$post_duplicates[ $_orig_lang ] = $duplicate_of;
$original_parent = get_comment_meta ( $comment[ 'comment_parent' ], '_icl_duplicate_of', true );
$wpdb->update (
$wpdb->comments,
array(
'comment_post_ID' => $duplicate_of,
'comment_parent' => $original_parent
), array( 'comment_ID' => $comment['comment_ID'] ), array( '%d', '%d' ), array( '%d' )
);
wp_update_comment_count_now($duplicate_of);
}
public function get_correct_parent($comment, $dup_id){
global $wpdb;
$translated_parent = $wpdb->get_var (
$wpdb->prepare (
" SELECT cmb.comment_id
FROM {$wpdb->commentmeta} cm
JOIN {$wpdb->commentmeta} cmb
ON ( cmb.meta_value = cm.meta_value
AND cmb.meta_key = cm.meta_key)
OR cm.comment_id = cmb.meta_value
JOIN {$wpdb->comments} c
ON c.comment_ID = cmb.comment_id
WHERE cm.meta_key = '_icl_duplicate_of'
AND ( cm.comment_id = %d OR cm.meta_value = %d )
AND c.comment_post_ID = %d",
$comment[ 'comment_parent' ],
$comment[ 'comment_parent' ],
$dup_id
)
);
return $translated_parent;
}
public function insert_duplicated_comment( $comment, $dup_id, $original_cid ) {
global $wpdb, $iclTranslationManagement;
$dup_comment_id = $this->duplicate_exists ( $dup_id, $original_cid );
remove_action ( 'wp_insert_comment', array( $iclTranslationManagement, 'duplication_insert_comment' ), 100 );
if ( $dup_comment_id ) {
$comment[ 'comment_ID' ] = $dup_comment_id;
wp_update_comment ( $comment );
} else {
$wpdb->insert ( $wpdb->comments, $comment );
$dup_comment_id = $wpdb->insert_id;
add_action ( 'wp_insert_comment', array( $iclTranslationManagement, 'duplication_insert_comment' ), 100 );
update_comment_meta ( $dup_comment_id, '_icl_duplicate_of', $original_cid );
// comment meta
$meta = $wpdb->get_results (
$wpdb->prepare (
"SELECT meta_key, meta_value FROM {$wpdb->commentmeta} WHERE comment_id=%d",
$original_cid
)
);
foreach ( $meta as $meta_row ) {
$wpdb->insert (
$wpdb->commentmeta,
array(
'comment_id' => $dup_comment_id,
'meta_key' => $meta_row->meta_key,
'meta_value' => $meta_row->meta_value
), array( '%d', '%s', '%s' )
);
}
}
wp_update_comment_count_now ( $dup_id );
}
private function duplicate_exists( $dup_id, $original_cid ) {
global $wpdb;
$duplicate = $wpdb->get_var (
$wpdb->prepare (
" SELECT comm.comment_ID
FROM {$wpdb->comments} comm
JOIN {$wpdb->commentmeta} cm
ON comm.comment_ID = cm.comment_id
WHERE comm.comment_post_ID = %d
AND cm.meta_key = '_icl_duplicate_of'
AND cm.meta_value = %d
LIMIT 1",
$dup_id,
$original_cid
)
);
return $duplicate;
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* Class WPML_Create_Post_Helper
*
* @since 3.2
*/
class WPML_Create_Post_Helper {
/** @var SitePress $sitepress */
private $sitepress;
public function __construct( SitePress $sitepress ) {
$this->sitepress = $sitepress;
}
/**
* @param array $postarr will be escaped inside the method
* @param string|null $lang
* @param bool $wp_error
*
* @return int|WP_Error
*/
public function insert_post( array $postarr, $lang = null, $wp_error = false ) {
$current_language = null;
$postarr = $this->slash_and_preserve_tag_ids( $postarr );
if ( $lang ) {
$current_language = $this->sitepress->get_current_language();
$this->sitepress->switch_lang( $lang, false );
}
if ( isset( $postarr['ID'] ) ) {
$new_post_id = wp_update_post( $postarr, $wp_error );
} else {
add_filter( 'wp_insert_post_empty_content', array( $this, 'allow_empty_post' ), 10, 0 );
$new_post_id = wp_insert_post( $postarr, $wp_error );
remove_filter( 'wp_insert_post_empty_content', array( $this, 'allow_empty_post' ) );
}
if ( $current_language ) {
$this->sitepress->switch_lang( $current_language, false );
}
return $new_post_id;
}
public function allow_empty_post() {
return false; // We need to return false to indicate that the post is not empty
}
/**
* We need to make sure that tag IDs are not casted into strings.
* This is a side effect of https://core.trac.wordpress.org/ticket/45121
* (wp_update_post() can modify post tag) for which we have
* a temporary fix in `\WPML_Page_Builders_Media_Shortcodes_Update::translate`.
*
* @param array $postarr
*
* @return array
*/
private function slash_and_preserve_tag_ids( array $postarr ) {
if ( array_key_exists( 'tags_input', $postarr ) ) {
$tagIds = array_filter( $postarr['tags_input'], 'is_int' );
$postarr = wp_slash( $postarr );
$postarr['tags_input'] = array_merge( $tagIds, $this->parse_tag( $postarr['tags_input'] ) );
} else {
$postarr = wp_slash( $postarr );
}
return $postarr;
}
private function parse_tag( $tags ) {
if ( empty( $tags ) ) {
$tags = array();
}
if ( ! is_array( $tags ) ) {
$comma = _x( ',', 'tag delimiter' );
if ( ',' !== $comma ) {
$tags = str_replace( $comma, ',', $tags );
}
$tags = explode( ',', trim( $tags, " \n\t\r\0\x0B," ) );
}
return $tags;
}
}

View File

@@ -0,0 +1,69 @@
<?php
use WPML\LIB\WP\Hooks;
class WPML_Frontend_Post_Actions extends WPML_Post_Translation {
public function init() {
parent::init ();
if ( $this->is_setup_complete() ) {
/**
* In theory, we could add the 'delete_post` action for all frontend requests
* We'll limit it to REST to avoid any unexpected problems
*/
Hooks::onAction( 'rest_api_init' )
->then( function () {
add_action( 'delete_post', [ $this, 'delete_post_actions' ] );
} );
}
}
/**
* @param int $post_id
* @param string $post_status
*
* @return null|int
*/
function get_save_post_trid( $post_id, $post_status ) {
return $this->get_element_trid( $post_id );
}
/**
* @param int $pidd
* @param WP_Post $post
*
* @return void
*/
public function save_post_actions( $pidd, $post ) {
global $sitepress;
$this->defer_term_counting();
if ( ! $post ) {
$post = get_post( $pidd );
}
$http_referer = new WPML_URL_HTTP_Referer( new WPML_Rest( new WP_Http() ) );
// exceptions
if ( ! $this->has_save_post_action( $post ) || $http_referer->is_rest_request_called_from_post_edit_page() ) {
return;
}
$default_language = $sitepress->get_default_language();
$post_vars = $this->get_post_vars( $post );
$post_id = isset( $post_vars['post_ID'] ) ? $post_vars['post_ID']
: $pidd; //latter case for XML-RPC publishing
$language_code = $this->get_save_post_lang( $post_id, $sitepress );
$trid = $this->get_save_post_trid( $post_id, $post->post_status );
// after getting the right trid set the source language from it by referring to the root translation
// of this trid, in case no proper source language has been set yet
$source_language = $this->get_save_post_source_lang( $trid, $language_code, $default_language );
$this->after_save_post( $trid, $post_vars, $language_code, $source_language );
}
protected function get_save_post_source_lang( $trid, $language_code, $default_language ) {
$post_id = $this->get_element_id ( $trid, $language_code );
return $post_id ? $this->get_source_lang_code ( $post_id ) : null;
}
}

View File

@@ -0,0 +1,383 @@
<?php
require_once dirname( __FILE__ ) . '/wpml-wordpress-actions.class.php';
/**
* Class WPML_Post_Duplication
*
* @package wpml-core
* @subpackage post-translation
*/
class WPML_Post_Duplication extends WPML_WPDB_And_SP_User {
function get_duplicates( $master_post_id ) {
global $wpml_post_translations;
$duplicates = array();
$post_ids_query = " SELECT post_id
FROM {$this->wpdb->postmeta}
WHERE meta_key='_icl_lang_duplicate_of'
AND meta_value = %d
AND post_id <> %d";
$post_ids_prepare = $this->wpdb->prepare( $post_ids_query, array( $master_post_id, $master_post_id ) );
$post_ids = $this->wpdb->get_col( $post_ids_prepare );
foreach ( $post_ids as $post_id ) {
$language_code = $wpml_post_translations->get_element_lang_code( $post_id );
if ( ! $language_code ) {
continue;
}
$duplicates[ $language_code ] = $post_id;
}
return $duplicates;
}
function make_duplicate( $master_post_id, $lang ) {
global $wpml_post_translations;
/**
* @deprecated Use 'wpml_before_make_duplicate' instead
* @since 3.4
*/
do_action( 'icl_before_make_duplicate', $master_post_id, $lang );
do_action( 'wpml_before_make_duplicate', $master_post_id, $lang );
$master_post = get_post( $master_post_id );
if ( ! $master_post || $this->is_external( $master_post_id ) ) {
return true;
}
$is_duplicated = false;
$translations = $wpml_post_translations->get_element_translations( $master_post_id, false, false );
if ( isset( $translations[ $lang ] ) ) {
$post_array[ 'ID' ] = $translations[ $lang ];
if ( WPML_WordPress_Actions::is_bulk_trash( $post_array[ 'ID' ] ) || WPML_WordPress_Actions::is_bulk_untrash( $post_array[ 'ID' ] ) ) {
return true;
}
$is_duplicated = get_post_meta( $translations[ $lang ], '_icl_lang_duplicate_of', true );
}
$post_array['post_author'] = $master_post->post_author;
$post_array['post_date'] = $master_post->post_date;
$post_array['post_date_gmt'] = $master_post->post_date_gmt;
$duplicated_post_content = $this->duplicate_post_content( $lang, $master_post );
$post_array['post_content'] = $duplicated_post_content;
$duplicated_post_title = $this->duplicate_post_title( $lang, $master_post );
$post_array['post_title'] = $duplicated_post_title;
$duplicated_post_excerpt = $this->duplicate_post_excerpt( $lang, $master_post );
$post_array['post_excerpt'] = $duplicated_post_excerpt;
if ( $this->sitepress->get_setting('sync_post_status' ) ) {
$sync_post_status = true;
} else {
$sync_post_status = ( ! isset( $post_array[ 'ID' ] )
|| ( $this->sitepress->get_setting( 'sync_delete' ) && $master_post->post_status === 'trash' ) || $is_duplicated );
}
if ( $sync_post_status || ( isset( $post_array[ 'ID' ] ) && get_post_status( $post_array[ 'ID' ] ) === 'auto-draft' ) ) {
$post_array[ 'post_status' ] = $master_post->post_status;
}
$post_array[ 'comment_status' ] = $master_post->comment_status;
$post_array[ 'ping_status' ] = $master_post->ping_status;
$post_array[ 'post_name' ] = $master_post->post_name;
if ( $master_post->post_parent ) {
$parent = $this->sitepress->get_object_id( $master_post->post_parent, $master_post->post_type, false, $lang );
$post_array[ 'post_parent' ] = $parent;
}
$post_array['menu_order'] = $master_post->menu_order;
$post_array['post_type'] = $master_post->post_type;
$post_array['post_mime_type'] = $master_post->post_mime_type;
$trid = $this->sitepress->get_element_trid( $master_post->ID,
'post_' . $master_post->post_type );
$id = $this->save_duplicate( $post_array, $lang );
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear();
global $ICL_Pro_Translation;
/** @var WPML_Pro_Translation $ICL_Pro_Translation */
if ( $ICL_Pro_Translation ) {
$ICL_Pro_Translation->fix_links_to_translated_content( $id, $lang );
}
if ( ! is_wp_error( $id ) ) {
$ret = $this->run_wpml_actions( $master_post, $trid, $lang, $id, $post_array );
} else {
throw new Exception( $id->get_error_message() );
}
return $ret;
}
/**
* @param int $element_id
*
* @return null|string
*/
private function is_external( $element_id ) {
$query = "SELECT element_type FROM {$this->wpdb->prefix}icl_translations WHERE element_id=%d AND element_type LIKE %s LIMIT 1";
return ! $this->wpdb->get_var( $this->wpdb->prepare( $query, $element_id, 'post_%' ) );
}
private function run_wpml_actions( $master_post, $trid, $lang, $id, $post_array ) {
$master_post_id = $master_post->ID;
$this->sitepress->set_element_language_details( $id, 'post_' . $master_post->post_type, $trid, $lang );
$this->sync_duplicate_password( $master_post_id, $id );
$this->sync_page_template( $master_post_id, $id );
$this->duplicate_fix_children( $master_post_id, $lang );
// make sure post name is copied
$this->wpdb->update( $this->wpdb->posts, array( 'post_name' => $master_post->post_name ), array( 'ID' => $id ) );
if ( $this->sitepress->get_setting( 'sync_post_taxonomies', false ) ) {
$this->duplicate_taxonomies( $master_post_id, $lang );
}
$this->duplicate_custom_fields( $master_post_id, $lang );
update_post_meta( $id, '_icl_lang_duplicate_of', $master_post->ID );
// Duplicate post format after the taxonomies because post format is stored
// as a taxonomy by WP.
if ( $this->sitepress->get_setting( 'sync_post_format' ) ) {
$_wp_post_format = get_post_format( $master_post_id );
set_post_format( $id, $_wp_post_format );
}
if ( $this->sitepress->get_setting( 'sync_comments_on_duplicates' ) ) {
$this->duplicate_comments( $master_post_id, $id );
}
$status_helper = wpml_get_post_status_helper();
$status_helper->set_status( $id, ICL_TM_DUPLICATE );
$status_helper->set_update_status( $id, false );
do_action( 'icl_make_duplicate', $master_post_id, $lang, $post_array, $id );
clean_post_cache( $id );
return $id;
}
private function sync_page_template( $master_post_id, $duplicate_post_id ) {
$_wp_page_template = get_post_meta( $master_post_id, '_wp_page_template', true );
if ( ! empty( $_wp_page_template ) ) {
update_post_meta( $duplicate_post_id, '_wp_page_template', $_wp_page_template );
}
}
private function duplicate_comments( $master_post_id, $translated_id ) {
global $sitepress;
remove_filter( 'comments_clauses', array( $sitepress, 'comments_clauses' ), 10 );
$comments_on_master = get_comments( array( 'post_id' => $master_post_id ) );
$comments_on_translation = get_comments( array( 'post_id' => $translated_id, 'status' => 'any' ) );
add_filter( 'comments_clauses', array( $sitepress, 'comments_clauses' ), 10, 2 );
foreach ( $comments_on_translation as $comment ) {
wp_delete_comment( $comment->comment_ID, true );
clean_comment_cache( $comment->comment_ID );
}
$iclTranslationManagement = wpml_load_core_tm();
foreach ( $comments_on_master as $comment ) {
$iclTranslationManagement->duplication_insert_comment( $comment->comment_ID );
clean_comment_cache( $comment->comment_ID );
}
wp_update_comment_count_now( $master_post_id );
wp_update_comment_count_now( $translated_id );
}
/**
* @param array $post_array
* @param string $lang
*
* @return int|WP_Error
*/
private function save_duplicate( array $post_array, $lang ) {
return wpml_get_create_post_helper()->insert_post( $post_array, $lang, true );
}
private function duplicate_fix_children( $master_post_id, $lang ) {
$post_type = $this->wpdb->get_var(
$this->wpdb->prepare( "SELECT post_type FROM {$this->wpdb->posts} WHERE ID=%d", $master_post_id )
);
$master_children = $this->wpdb->get_col(
$this->wpdb->prepare(
"SELECT ID FROM {$this->wpdb->posts} WHERE post_parent=%d AND post_type != 'revision'",
$master_post_id
)
);
$dup_parent = icl_object_id( $master_post_id, $post_type, false, $lang );
if ( $master_children ) {
foreach ( $master_children as $master_child ) {
$dup_child = icl_object_id( $master_child, $post_type, false, $lang );
if ( $dup_child ) {
$this->wpdb->update( $this->wpdb->posts, array( 'post_parent' => $dup_parent ), array( 'ID' => $dup_child ) );
}
$this->duplicate_fix_children( $master_child, $lang );
}
}
}
private function duplicate_taxonomies( $master_post_id, $lang ) {
$post_type = get_post_field( 'post_type', $master_post_id );
$taxonomies = get_object_taxonomies( $post_type );
$trid = $this->sitepress->get_element_trid( $master_post_id, 'post_' . $post_type );
if ( $trid ) {
$translations = $this->sitepress->get_element_translations( $trid, 'post_' . $post_type, false, false, true );
if ( isset( $translations[ $lang ] ) ) {
$duplicate_post_id = $translations[ $lang ]->element_id;
/* If we have an existing post, we first of all remove all terms currently attached to it.
* The main reason behind is the removal of the potentially present default category on the post.
*/
wp_delete_object_term_relationships( $duplicate_post_id, $taxonomies );
} else {
return false; // translation not found!
}
}
$term_helper = wpml_get_term_translation_util();
$term_helper->duplicate_terms( $master_post_id, $lang );
return true;
}
private function sync_duplicate_password( $master_post_id, $duplicate_post_id ) {
if ( post_password_required( $master_post_id ) ) {
$sql = $this->wpdb->prepare( "UPDATE {$this->wpdb->posts} AS dupl,
(SELECT org.post_password FROM {$this->wpdb->posts} AS org WHERE ID = %d ) AS pwd
SET dupl.post_password = pwd.post_password
WHERE dupl.ID = %d",
array( $master_post_id, $duplicate_post_id ) );
$this->wpdb->query( $sql );
}
}
private function duplicate_custom_fields( $master_post_id, $lang ) {
$duplicate_post_id = false;
$post_type = get_post_field( 'post_type', $master_post_id );
$trid = $this->sitepress->get_element_trid( $master_post_id, 'post_' . $post_type );
if ( $trid ) {
$translations = $this->sitepress->get_element_translations( $trid, 'post_' . $post_type );
if ( isset( $translations[ $lang ] ) ) {
$duplicate_post_id = $translations[ $lang ]->element_id;
} else {
return false; // translation not found!
}
}
$default_exceptions = WPML_Config::get_custom_fields_translation_settings();
$exceptions = apply_filters( 'wpml_duplicate_custom_fields_exceptions', array() );
$exceptions = array_merge( $exceptions, $default_exceptions );
$exceptions = array_unique( $exceptions );
$exceptions_in = ! empty( $exceptions )
? 'AND meta_key NOT IN ( ' . wpml_prepare_in( $exceptions ) . ') ' : '';
$from_where_string = "FROM {$this->wpdb->postmeta} WHERE post_id = %d " . $exceptions_in;
$post_meta_master = $this->wpdb->get_results( "SELECT meta_key, meta_value " . $this->wpdb->prepare( $from_where_string,
$master_post_id ) );
$this->wpdb->query( "DELETE " . $this->wpdb->prepare( $from_where_string, $duplicate_post_id ) );
$values = [];
foreach ( $post_meta_master as $post_meta ) {
$is_serialized = is_serialized( $post_meta->meta_value );
$meta_data = array(
'context' => 'custom_field',
'attribute' => 'value',
'key' => $post_meta->meta_key,
'is_serialized' => $is_serialized,
'post_id' => $duplicate_post_id,
'master_post_id' => $master_post_id,
);
/**
* @deprecated use 'wpml_duplicate_generic_string' instead, with the same arguments
*/
$icl_duplicate_generic_string = apply_filters( 'icl_duplicate_generic_string',
$post_meta->meta_value,
$lang,
$meta_data );
$post_meta->meta_value = $icl_duplicate_generic_string;
$wpml_duplicate_generic_string = apply_filters( 'wpml_duplicate_generic_string',
$post_meta->meta_value,
$lang,
$meta_data );
$post_meta->meta_value = $wpml_duplicate_generic_string;
if ( ! is_serialized( $post_meta->meta_value ) ) {
$post_meta->meta_value = maybe_serialize( $post_meta->meta_value );
}
$values[] = $this->wpdb->prepare(
'( %d, %s, %s )',
$duplicate_post_id,
$post_meta->meta_key,
$post_meta->meta_value
);
}
if ( ! empty( $values ) ) {
$values = implode( ', ', $values );
$this->wpdb->query(
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"INSERT INTO `{$this->wpdb->postmeta}` (`post_id`, `meta_key`, `meta_value`) VALUES {$values}"
);
}
return true;
}
/**
* @param string $lang
* @param \WP_Post $master_post
*
* @return array<string,mixed>
*/
private function duplicate_post_content( $lang, $master_post ) {
$duplicated_post_content_meta = array(
'context' => 'post',
'attribute' => 'content',
'key' => $master_post->ID
);
$duplicated_post_content = $master_post->post_content;
$duplicated_post_content = apply_filters( 'icl_duplicate_generic_string', $duplicated_post_content, $lang, $duplicated_post_content_meta );
$duplicated_post_content = apply_filters( 'wpml_duplicate_generic_string', $duplicated_post_content, $lang, $duplicated_post_content_meta );
return $duplicated_post_content;
}
/**
* @param string $lang
* @param \WP_Post $master_post
*
* @return mixed
*/
private function duplicate_post_title( $lang, $master_post ) {
$duplicated_post_title_meta = array(
'context' => 'post',
'attribute' => 'title',
'key' => $master_post->ID
);
$duplicated_post_title = $master_post->post_title;
$duplicated_post_title = apply_filters( 'icl_duplicate_generic_string', $duplicated_post_title, $lang, $duplicated_post_title_meta );
$duplicated_post_title = apply_filters( 'wpml_duplicate_generic_string', $duplicated_post_title, $lang, $duplicated_post_title_meta );
return $duplicated_post_title;
}
/**
* @param string $lang
* @param WP_Post $master_post
*
* @return mixed
*/
private function duplicate_post_excerpt( $lang, $master_post ) {
$duplicated_post_excerpt_meta = array(
'context' => 'post',
'attribute' => 'excerpt',
'key' => $master_post->ID
);
$duplicated_post_excerpt = $master_post->post_excerpt;
$duplicated_post_excerpt = apply_filters( 'icl_duplicate_generic_string',
$duplicated_post_excerpt,
$lang,
$duplicated_post_excerpt_meta );
$duplicated_post_excerpt = apply_filters( 'wpml_duplicate_generic_string',
$duplicated_post_excerpt,
$lang,
$duplicated_post_excerpt_meta );
return $duplicated_post_excerpt;
}
}

View File

@@ -0,0 +1,27 @@
<?php
class WPML_Post_Hierarchy_Sync extends WPML_Hierarchy_Sync {
protected $element_id_column = 'ID';
protected $parent_element_id_column = 'ID';
protected $parent_id_column = 'post_parent';
protected $element_type_column = 'post_type';
protected $element_type_prefix = 'post_';
/**
* @param wpdb $wpdb
*/
public function __construct( &$wpdb ) {
parent::__construct( $wpdb );
$this->elements_table = $wpdb->posts;
}
/**
* @param string $element_type
*
* @return bool
*/
public function is_hierarchical( $element_type ) {
return is_post_type_hierarchical( $element_type );
}
}

View File

@@ -0,0 +1,364 @@
<?php
use WPML\FP\Lst;
use WPML\FP\Maybe;
use WPML\FP\Obj;
use function WPML\FP\partial;
/**
* Class WPML_Post_Synchronization
*
* @package wpml-core
* @subpackage post-translation
*/
class WPML_Post_Synchronization extends WPML_SP_And_PT_User {
/** @var bool[] */
private $sync_parent_cpt = array();
/** @var bool $sync_parent */
private $sync_parent;
/** @var bool $sync_delete */
private $sync_delete;
/** @var bool $sync_ping_status */
private $sync_ping_status;
/** @var bool $sync_post_date */
private $sync_post_date;
/** @var bool $sync_post_format */
private $sync_post_format;
/** @var bool $sync_comment_status */
private $sync_comment_status;
/** @var bool $sync_page_template */
private $sync_page_template;
/** @var bool $sync_menu_order */
private $sync_menu_order;
/** @var bool $sync_password */
private $sync_password;
/** @var bool $sync_private_flag */
private $sync_private_flag;
/** @var bool $is_deleting_all_translations */
private $is_deleting_all_translations = false;
/** @var array $deleted_post_types */
private $deleted_post_types = array();
/**
* @var int
*/
private $sync_document_status;
/**
* @param array $settings
* @param WPML_Post_Translation $post_translations
* @param SitePress $sitepress
*/
public function __construct( &$settings, &$post_translations, &$sitepress ) {
parent::__construct( $post_translations, $sitepress );
$this->sync_delete = isset( $settings[ 'sync_delete' ] ) ? $settings[ 'sync_delete' ] : false;
$this->sync_parent = isset( $settings[ 'sync_page_parent' ] ) ? $settings[ 'sync_page_parent' ] : false;
$this->sync_ping_status = isset( $settings[ 'sync_ping_status' ] ) ? $settings[ 'sync_ping_status' ] : false;
$this->sync_post_date = isset( $settings[ 'sync_post_date' ] ) ? $settings[ 'sync_post_date' ] : false;
$this->sync_post_format = isset( $settings[ 'sync_post_format' ] ) ? $settings[ 'sync_post_format' ] : false;
$this->sync_comment_status = isset( $settings[ 'sync_comment_status' ] ) ? $settings[ 'sync_comment_status' ] : false;
$this->sync_page_template = isset( $settings[ 'sync_page_template' ] ) ? $settings[ 'sync_page_template' ] : false;
$this->sync_password = isset( $settings[ 'sync_password' ] ) ? $settings[ 'sync_password' ] : false;
$this->sync_private_flag = isset( $settings[ 'sync_private_flag' ] ) ? $settings[ 'sync_private_flag' ] : false;
$this->sync_document_status = isset( $settings[ 'translated_document_status' ] ) ? $settings[ 'translated_document_status' ] : 1;
$this->sync_menu_order = isset( $settings[ 'sync_page_ordering' ] ) ? $settings[ 'sync_page_ordering' ] : array();
}
private function must_sync_parents( $post_type ) {
if ( ! array_key_exists( $post_type, $this->sync_parent_cpt ) ) {
$this->sync_parent_cpt[ $post_type ] = apply_filters( 'wpml_sync_parent_for_post_type', $this->sync_parent, $post_type );
}
return $this->sync_parent_cpt[ $post_type ];
}
/**
* Fixes parents of translations for hierarchical post types
*
* User changed parent for a post in $post_type and we are setting proper parent for $translation_id in
* $language_code_translated language
*
* @param string $post_type - post_type that should have the translated parents fixed
*/
private function maybe_fix_translated_parent( $post_type ) {
if ( $this->must_sync_parents( $post_type ) ) {
$sync_helper = wpml_get_hierarchy_sync_helper();
$sync_helper->sync_element_hierarchy( $post_type );
}
}
public function sync_with_duplicates( $post_id ) {
$duplicates = $this->sitepress->get_duplicates( $post_id );
foreach ( array_keys( $duplicates ) as $lang_code ) {
$this->sitepress->make_duplicate( $post_id, $lang_code );
}
}
/**
* @param int $post_id
* @param bool $keep_db_entries
*/
public function delete_post_actions( $post_id, $keep_db_entries = false ) {
$post_type = get_post_type( $post_id );
$post_type_exceptions = array( 'nav_menu_item' );
if ( in_array( $post_type, $post_type_exceptions ) ) {
return;
}
$trid = null;
if ( ! $this->is_deleting_all_translations ) {
$this->is_deleting_all_translations = ! $this->post_translation->get_original_element( $post_id, true );
$trid = $this->post_translation->get_element_trid( $post_id );
$translated_ids = $this->get_translations_without_source( $post_id, $trid );
if ( $this->sync_delete || Lst::includes( $post_type, [ 'wp_template', 'wp_template_part' ] ) ) {
$this->delete_translations( $translated_ids, $keep_db_entries );
}
$this->is_deleting_all_translations = false;
}
if ( ! $keep_db_entries ) {
$this->post_translation->delete_post_translation_entry( $post_id );
if ( $trid && ! $this->is_deleting_all_translations ) {
$lang_code = $this->post_translation->get_element_lang_code( $post_id );
$this->set_new_original( $trid, $lang_code );
}
}
if ( ! $this->is_deleting_all_translations ) {
$this->run_final_actions_for_delete_post( $post_type );
}
}
/**
* @param int $post_id
* @param int $trid
*
* @return array
*/
private function get_translations_without_source( $post_id, $trid ) {
$actual_translations_only = ! $this->is_deleting_all_translations;
$translated_ids = $this->post_translation->get_element_translations( $post_id, $trid, $actual_translations_only );
unset( $translated_ids[ array_search( $post_id, $translated_ids ) ] );
return $translated_ids;
}
private function is_bulk_delete() {
return ( isset( $_REQUEST['action'] ) && 'delete' === $_REQUEST['action']
|| isset( $_REQUEST['action2'] ) && 'delete' === $_REQUEST['action2']
) && ( isset( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] )
|| isset( $_REQUEST['media'] ) && is_array( $_REQUEST['media'] )
);
}
/** @param string $post_type */
private function reset_cache( $post_type ) {
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear( $post_type . 's_per_language', true );
}
/** @param string $post_type */
private function defer_delete_actions( $post_type ) {
if ( ! in_array( $post_type, $this->deleted_post_types, true ) ) {
$this->deleted_post_types[] = $post_type;
if ( ! has_action( 'shutdown', array( $this, 'shutdown_action' ) ) ) {
add_action( 'shutdown', array( $this, 'shutdown_action' ) );
}
}
}
public function shutdown_action() {
$this->post_translation->reload();
foreach ( $this->deleted_post_types as $post_type ) {
$this->reset_cache( $post_type );
$this->maybe_fix_translated_parent( $post_type );
}
}
/**
* @param array $translated_ids
* @param bool $keep_db_entries
*/
private function delete_translations( array $translated_ids, $keep_db_entries ) {
if ( ! empty( $translated_ids ) ) {
foreach ( $translated_ids as $trans_id ) {
if ( ! $this->is_bulk_prevented( $trans_id ) ) {
if ( $keep_db_entries ) {
$this->post_translation->trash_translation( $trans_id );
} else {
wp_delete_post( $trans_id, true );
}
}
}
}
}
/** @param string $post_type */
private function run_final_actions_for_delete_post( $post_type ) {
if ( $this->is_bulk_delete() ) {
$this->defer_delete_actions( $post_type );
} else {
$this->post_translation->reload();
$this->reset_cache( $post_type );
$this->maybe_fix_translated_parent( $post_type );
}
}
private function is_bulk_prevented( $post_id ) {
return ( isset( $_GET[ 'delete_all' ] ) && $_GET[ 'delete_all' ] === 'Empty Trash' )
|| in_array( $post_id, ( isset( $_GET[ 'ids' ] ) ? $_GET[ 'ids' ] : array() ) );
}
function untrashed_post_actions( $post_id ) {
if ( $this->sync_delete ) {
$translations = $this->post_translation->get_element_translations( $post_id, false, true );
foreach ( $translations as $t_id ) {
$this->post_translation->untrash_translation( $t_id );
}
}
$post_type = get_post_type( $post_id );
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
icl_cache_clear( $post_type . 's_per_language', true );
}
public function sync_with_translations( $post_id, $post_vars = false ) {
global $wpdb;
$wp_api = $this->sitepress->get_wp_api();
$term_count_update = new WPML_Update_Term_Count( $wp_api );
$post = get_post ( $post_id );
$source_post_status = $this->get_post_status( $post_id );
$translated_ids = $this->post_translation->get_element_translations( $post_id, false, true );
$post_format = $this->sync_post_format ? get_post_format( $post_id ) : null;
$ping_status = $this->sync_ping_status ? ( pings_open( $post_id ) ? 'open' : 'closed' ) : null;
$comment_status = $this->sync_comment_status ? ( comments_open( $post_id ) ? 'open' : 'closed' ) : null;
$post_password = $this->sync_password ? $post->post_password : null;
$menu_order = $this->sync_menu_order && ! empty( $post->menu_order ) ? $post->menu_order : null;
$page_template = $this->sync_page_template && get_post_type( $post_id ) === 'page' ? get_post_meta( $post_id, '_wp_page_template', true ) : null;
$post_date = $this->sync_post_date ? $wpdb->get_var( $wpdb->prepare( "SELECT post_date FROM {$wpdb->posts} WHERE ID=%d LIMIT 1", $post_id ) ) : null;
foreach ( $translated_ids as $lang_code => $translated_pid ) {
$post_status = $this->get_post_status( $translated_pid );
$post_status_differs = ( 'private' === $source_post_status && 'publish' === $post_status )
|| ( 'publish' === $source_post_status && 'private' === $post_status );
if ( $this->sync_private_flag && $post_status_differs ) {
$post_status = $source_post_status;
}
$this->sync_custom_fields ( $post_id, $translated_pid );
if ( $post_format ) {
set_post_format ( $translated_pid, $post_format );
}
if ( $post_date !== null ) {
$post_date_gmt = get_gmt_from_date ( $post_date );
$data = array( 'post_date' => $post_date, 'post_date_gmt' => $post_date_gmt );
$now = gmdate('Y-m-d H:i:59');
$allow_post_statuses = array( 'private', 'pending', 'draft' );
if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) ) {
if ( ! in_array( $post_status, $allow_post_statuses, true ) ) {
$post_status = 'future';
}
}
$data[ 'post_status' ] = $post_status;
$wpdb->update ( $wpdb->posts, $data, array( 'ID' => $translated_pid ) );
wp_schedule_single_event( strtotime( $post_date_gmt . '+1 second' ), 'publish_future_post', array( $translated_pid ) );
}
if ( $post_password !== null ) {
$wpdb->update ( $wpdb->posts, array( 'post_password' => $post_password ), array( 'ID' => $translated_pid ) );
}
if ( $post_status !== null && ! in_array( $this->get_post_status( $translated_pid ), array( 'auto-draft', 'draft', 'inherit', 'trash' ) ) ) {
$wpdb->update ( $wpdb->posts, array( 'post_status' => $post_status ), array( 'ID' => $translated_pid ) );
$term_count_update->update_for_post( $translated_pid );
} elseif ( $post_status == null && $this->sync_private_flag && $this->get_post_status( $translated_pid ) === 'private' ) {
$wpdb->update ( $wpdb->posts, array( 'post_status' => $this->get_post_status( $post_id ) ), array( 'ID' => $translated_pid ) );
$term_count_update->update_for_post( $translated_pid );
}
if ( $ping_status !== null ) {
$wpdb->update ( $wpdb->posts, array( 'ping_status' => $ping_status ), array( 'ID' => $translated_pid ) );
}
if ( $comment_status !== null ) {
$wpdb->update ( $wpdb->posts, array( 'comment_status' => $comment_status ), array( 'ID' => $translated_pid ) );
}
if ( $page_template !== null ) {
update_post_meta ( $translated_pid, '_wp_page_template', $page_template );
}
$this->sync_with_translations ( $translated_pid );
}
$this->maybe_fix_translated_parent( get_post_type( $post_id ) );
if ( $menu_order !== null && (bool) $translated_ids !== false ) {
$query = $wpdb->prepare(
"UPDATE {$wpdb->posts}
SET menu_order=%s
WHERE ID IN (" . wpml_prepare_in( $translated_ids, '%d' ) . ')',
$menu_order
);
$wpdb->query( $query );
}
}
/**
* The function `get_post_status` does not return the raw status for attachments.
* As we are running direct DB updates here, we need the actual DB value.
*
* @param int $post_id
*
* @return string|false
*/
private function get_post_status( $post_id ) {
$isAttachment = function( $post_id ) { return 'attachment' === get_post_type( $post_id ); };
return Maybe::of( $post_id )
->filter( $isAttachment )
->map( 'get_post' )
->map( Obj::prop( 'post_status' ) )
->getOrElse( partial( 'get_post_status', $post_id ) );
}
private function sync_custom_fields( $original_id, $post_id ) {
if ( $original_id && $original_id != $post_id ) {
$this->sitepress->copy_custom_fields ( $original_id, $post_id );
} else {
$translations = $this->post_translation->get_element_translations ( $post_id, false, true );
foreach ( $translations as $t_id ) {
$this->sitepress->copy_custom_fields ( $post_id, $t_id );
}
}
}
private function set_new_original( $trid, $removed_lang_code ) {
if ( $trid && $removed_lang_code ) {
$priorities = $this->sitepress->get_setting( 'languages_order' );
$this->post_translation->reload();
$translations = $this->post_translation->get_element_translations( false, $trid );
$new_source_lang_code = false;
foreach ( $priorities as $lang_code ) {
if ( isset( $translations[ $lang_code ] ) ) {
$new_source_lang_code = $lang_code;
break;
}
}
if ( $new_source_lang_code ) {
global $wpdb;
$rows_updated = $wpdb->update( $wpdb->prefix . 'icl_translations',
array( 'source_language_code' => $new_source_lang_code ),
array( 'trid' => $trid, 'source_language_code' => $removed_lang_code )
);
if( 0 < $rows_updated ) {
do_action( 'wpml_translation_update', array( 'trid' => $trid ) );
}
$wpdb->query( " UPDATE {$wpdb->prefix}icl_translations
SET source_language_code = NULL
WHERE language_code = source_language_code" );
}
}
}
}

View File

@@ -0,0 +1,442 @@
<?php
/**
* Class WPML_Post_Translation
*
* @package wpml-core
* @subpackage post-translation
*/
abstract class WPML_Post_Translation extends WPML_Element_Translation {
protected $settings;
protected $post_translation_sync;
public static $defer_term_counting = false;
/**
* @var WPML_Debug_BackTrace
*/
private $debug_backtrace;
/**
* @param array $settings
* @param wpdb $wpdb
*/
public function __construct( &$settings, &$wpdb ) {
parent::__construct( $wpdb );
$this->settings = $settings;
}
protected function is_setup_complete( ) {
return isset( $this->settings[ 'setup_complete' ]) && $this->settings[ 'setup_complete' ];
}
public function init() {
if ( $this->is_setup_complete() ) {
add_action( 'save_post', [ $this, 'save_post_actions' ], 100, 2 );
add_action( 'shutdown', [ $this, 'shutdown_action' ], PHP_INT_MAX );
add_action( 'edit_attachment', [ $this, 'attachment_actions' ], 100 );
add_action( 'add_attachment', [ $this, 'attachment_actions' ], 100 );
}
}
public function get_original_post_status( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'post_status', $source_lang_code );
}
public function get_original_post_ID( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'ID', $source_lang_code );
}
public function get_original_menu_order
( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'menu_order', $source_lang_code );
}
public function get_original_comment_status( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'comment_status', $source_lang_code );
}
public function get_original_ping_status( $trid, $source_lang_code = null ) {
return $this->get_original_post_attr ( $trid, 'ping_status', $source_lang_code );
}
public function get_original_post_format( $trid, $source_lang_code = null ) {
return get_post_format ( $this->get_original_post_ID ( $trid, $source_lang_code ) );
}
/**
* @param int $pidd
* @param WP_Post $post
*
* @return void
*/
public abstract function save_post_actions( $pidd, $post );
/** @param int $post_id */
public function attachment_actions( $post_id ) {
/**
* This filter hooks determines whether we should apply the
* "save_post" actions on an attachment.
*
* @since 4.4.0
*
* @param bool True if we should apply save post actions on the attachment, false otherwise (default false).
* @param int $post_id The attachment post ID.
*/
if ( apply_filters( 'wpml_apply_save_attachment_actions', false, $post_id ) ) {
$post = get_post( $post_id );
if ( $post ) {
$this->save_post_actions( $post_id, $post );
}
}
}
public function shutdown_action() {
if ( self::$defer_term_counting ) {
self::$defer_term_counting = false;
wp_defer_term_counting( false );
}
}
public function trash_translation ( $trans_id ) {
if ( !WPML_WordPress_Actions::is_bulk_trash( $trans_id ) ) {
wp_trash_post( $trans_id );
}
}
public function untrash_translation( $trans_id ) {
if ( ! WPML_WordPress_Actions::is_bulk_untrash( $trans_id ) ) {
wp_untrash_post( $trans_id );
}
}
function untrashed_post_actions( $post_id ) {
$translation_sync = $this->get_sync_helper ();
$translation_sync->untrashed_post_actions ( $post_id );
}
public function delete_post_translation_entry( $post_id ) {
$update_args = array( 'context' => 'post', 'element_id' => $post_id );
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$sql = $this->wpdb->prepare( "DELETE FROM {$this->wpdb->prefix}icl_translations
WHERE element_id = %d
AND element_type LIKE 'post%%'
LIMIT 1",
$post_id );
$res = $this->wpdb->query( $sql );
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
return $res;
}
public function trashed_post_actions( $post_id ) {
$this->delete_post_actions( $post_id, true );
}
/**
* This function holds all actions to be run after deleting a post.
* 1. Delete the posts entry in icl_translations.
* 2. Set one of the posts translations or delete all translations of the post, depending on sitepress settings.
*
* @param Integer $post_id
* @param bool $keep_db_entries Sets whether icl_translations entries are to be deleted or kept, when hooking this to
* post trashing we want them to be kept.
*/
public function delete_post_actions( $post_id, $keep_db_entries = false ) {
$translation_sync = $this->get_sync_helper ();
$translation_sync->delete_post_actions ( $post_id, $keep_db_entries );
}
/**
* @param int $post_id
* @param string $post_status
*
* @return null|int
*/
abstract function get_save_post_trid( $post_id, $post_status );
/**
* @param integer $post_id
* @param SitePress $sitepress
* @return bool|mixed|null|string|void
*/
public function get_save_post_lang( $post_id, $sitepress ) {
$language_code = $this->get_element_lang_code ( $post_id );
$language_code = $language_code ? $language_code : $sitepress->get_current_language ();
$language_code = $sitepress->is_active_language ( $language_code ) ? $language_code
: $sitepress->get_default_language ();
return apply_filters ( 'wpml_save_post_lang', $language_code );
}
/**
* @param int $trid
* @param string $language_code
* @param string $default_language
*
* @return string|null
*/
protected abstract function get_save_post_source_lang( $trid, $language_code, $default_language );
/**
* Sets a posts language details, invalidates caches relating to the post and triggers
* synchronisation actions across translations of the just saved post.
*
* @param int $trid
* @param array $post_vars
* @param string $language_code
* @param string $source_language
*
* @used-by \WPML_Post_Translation::save_post_actions as final step of the WPML Core save_post actions
*/
protected function after_save_post( $trid, $post_vars, $language_code, $source_language ) {
$this->maybe_set_elid( $trid, $post_vars['post_type'], $language_code, $post_vars['ID'], $source_language );
$translation_sync = $this->get_sync_helper();
$original_id = $this->get_original_element( $post_vars['ID'] );
$translation_sync->sync_with_translations( $original_id ? $original_id : $post_vars['ID'], $post_vars );
$translation_sync->sync_with_duplicates( $post_vars['ID'] );
if ( ! function_exists( 'icl_cache_clear' ) ) {
require_once WPML_PLUGIN_PATH . '/inc/cache.php';
}
icl_cache_clear( $post_vars['post_type'] . 's_per_language', true );
if ( ! in_array( $post_vars['post_type'], array( 'nav_menu_item', 'attachment' ), true ) ) {
do_action( 'wpml_tm_save_post', $post_vars['ID'], get_post( $post_vars['ID'] ), false );
}
// Flush object cache.
$this->flush_object_cache_for_groups( array( 'ls_languages', 'element_translations' ) );
do_action( 'wpml_after_save_post', $post_vars['ID'], $trid, $language_code, $source_language );
}
/**
* Create new instance of WPML_WP_Cache for each group and flush cache for group.
* @param array $groups
*/
private function flush_object_cache_for_groups( $groups = array() ) {
if ( ! empty( $groups ) ) {
foreach ( $groups as $group ) {
$cache = new WPML_WP_Cache( $group );
$cache->flush_group_cache();
}
}
}
private function get_original_post_attr( $trid, $attribute, $source_lang_code ) {
$legal_attributes = array(
'post_status',
'post_date',
'menu_order',
'comment_status',
'ping_status',
'ID'
);
$res = false;
if ( in_array ( $attribute, $legal_attributes, true ) ) {
$attribute = 'p.' . $attribute;
$source_snippet = $source_lang_code === null
? " AND wpml_translations.source_language_code IS NULL "
: $this->wpdb->prepare ( " AND wpml_translations.language_code = %s ", $source_lang_code );
$res = $this->wpdb->get_var (
$this->wpdb->prepare (
"SELECT {$attribute}
" . $this->get_element_join() . "
WHERE wpml_translations.trid=%d
{$source_snippet}
LIMIT 1",
$trid
)
);
}
return $res;
}
public function has_save_post_action( $post ) {
if ( ! $post ) {
return false;
}
$is_auto_draft = isset( $post->post_status ) && $post->post_status === 'auto-draft';
$is_editing_different_post = $this->is_editing_different_post( $post->ID );
$is_saving_a_revision = array_key_exists( 'post_type', $_POST ) && 'revision' === $_POST['post_type'];
$is_untrashing = array_key_exists( 'action', $_GET ) && 'untrash' === $_GET['action'];
$is_auto_save = array_key_exists( 'autosave', $_POST );
$skip_sitepress_actions = array_key_exists( 'skip_sitepress_actions', $_POST );
$is_post_a_revision = 'revision' === $post->post_type;
$is_scheduled_to_be_trashed = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
$is_add_meta_action = isset( $_POST['action'] ) && 'add-meta' === $_POST['action'];
$is_inner_post_insertion = $this->is_inner_post_insertion();
return $this->is_translated_type( $post->post_type )
&& ! ( $is_auto_draft
|| $is_auto_save
|| $skip_sitepress_actions
|| ( $is_editing_different_post && ! $is_inner_post_insertion )
|| $is_saving_a_revision
|| $is_post_a_revision
|| $is_scheduled_to_be_trashed
|| $is_add_meta_action
|| $is_untrashing );
}
/**
* @param int $post_id
*
* @return bool
*/
protected function is_editing_different_post( $post_id ) {
return array_key_exists( 'post_ID', $_POST ) && (int) $_POST['post_ID'] && $post_id != $_POST['post_ID'];
}
protected function get_element_join() {
return "FROM {$this->wpdb->prefix}icl_translations wpml_translations
JOIN {$this->wpdb->posts} p
ON wpml_translations.element_id = p.ID
AND wpml_translations.element_type = CONCAT('post_', p.post_type)";
}
protected function get_type_prefix() {
return 'post_';
}
public function is_translated_type( $post_type ) {
global $sitepress;
return $sitepress->is_translated_post_type ( $post_type );
}
/**
* @param WP_Post $post
*
* @return string[] all language codes the post can be translated into
*/
public function get_allowed_target_langs( $post ) {
global $sitepress;
$active_languages = $sitepress->get_active_languages ();
$can_translate = array_keys ( $active_languages );
$can_translate = array_diff (
$can_translate,
array( $this->get_element_lang_code ( $post->ID ) )
);
return apply_filters ( 'wpml_allowed_target_langs', $can_translate, $post->ID, 'post' );
}
/**
* Before setting the language of the post to be saved, check if a translation in this language already exists
* This check is necessary, so that synchronization actions like thrashing or un-trashing of posts, do not lead to
* database corruption, due to erroneously changing a posts language into a state,
* where it collides with an existing translation. While the UI prevents this sort of action for the most part,
* this is not necessarily the case for other plugins like TM.
* The logic here first of all checks if an existing translation id is present in the desired language_code.
* If so but this translation is actually not the currently to be saved post,
* then this post will be saved to its current language. If the translation already exists,
* the existing translation id will be used. In all other cases a new entry in icl_translations will be created.
*
* @param Integer $trid
* @param String $post_type
* @param String $language_code
* @param Integer $post_id
* @param String $source_language
*/
private function maybe_set_elid( $trid, $post_type, $language_code, $post_id, $source_language ) {
global $sitepress;
$element_type = 'post_' . $post_type;
$sitepress->set_element_language_details (
$post_id,
$element_type,
$trid,
$language_code,
$source_language
);
}
/**
* @return WPML_Post_Synchronization
*/
private function get_sync_helper() {
global $sitepress;
$this->post_translation_sync = $this->post_translation_sync
? $this->post_translation_sync : new WPML_Post_Synchronization( $this->settings, $this, $sitepress );
return $this->post_translation_sync;
}
/**
* @return WPML_Debug_BackTrace
*/
private function get_debug_backtrace() {
if ( ! $this->debug_backtrace ) {
$this->debug_backtrace = new WPML\Utils\DebugBackTrace( 20 );
}
return $this->debug_backtrace;
}
public function set_debug_backtrace( WPML_Debug_BackTrace $debug_backtrace ) {
$this->debug_backtrace = $debug_backtrace;
}
/**
* @return bool
*/
protected function is_inner_post_insertion() {
$debug_backtrace = $this->get_debug_backtrace();
return 1 < $debug_backtrace->count_function_in_call_stack( 'wp_insert_post' );
}
/**
* @param WP_Post $post
*
* @return array
*/
protected function get_post_vars( $post ) {
$post_vars = array();
if ( ! $this->is_inner_post_insertion() ) {
$post_vars = (array) $_POST;
}
foreach ( (array) $post as $k => $v ) {
$post_vars[ $k ] = $v;
}
$post_vars['post_type'] = isset( $post_vars['post_type'] ) ? $post_vars['post_type'] : $post->post_type;
return $post_vars;
}
protected function defer_term_counting() {
if ( ! self::$defer_term_counting ) {
self::$defer_term_counting = true;
wp_defer_term_counting( true );
}
}
/**
* @return self|WPML_Frontend_Post_Actions|WPML_Admin_Post_Actions
*/
public static function getGlobalInstance() {
global $wpml_post_translations, $sitepress;
if ( ! isset( $wpml_post_translations ) ) {
wpml_load_post_translation( is_admin(), $sitepress->get_settings() );
}
return $wpml_post_translations;
}
}

View File

@@ -0,0 +1,310 @@
<?php
class WPML_Root_Page_Actions {
/** @var array $sp_settings */
private $sp_settings;
public function __construct( &$sitepress_settings ) {
$this->sp_settings = &$sitepress_settings;
}
public function delete_root_page_lang() {
global $wpdb;
$root_id = $this->get_root_page_id ();
if ( $root_id ) {
$update_args = array(
'element_id' => $root_id,
'element_type' => 'post_page',
'context' => 'post'
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$wpdb->delete (
$wpdb->prefix . 'icl_translations', array( 'element_id' => $root_id, 'element_type' => 'post_page' ), array( '%d', '%s' )
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
}
}
/**
* Checks if a given $url points at the root page
*
* @param string $url
*
* @return bool
*
* @uses \WPML_Root_Page::is_root_page
*/
public function is_url_root_page( $url ) {
$ret = false;
if ( $this->get_root_page_id() ) {
$ret = WPML_Root_Page::is_root_page( $url );
}
return $ret;
}
/**
* If a page is used as the root page, returns the id of that page, otherwise false.
*
* @return bool|false|int
*/
public function get_root_page_id() {
$urls_in_dirs = isset($this->sp_settings['language_negotiation_type']) && (int)$this->sp_settings['language_negotiation_type'] === 1;
$urls = isset( $this->sp_settings['urls'] ) ? $this->sp_settings['urls'] : array();
return $urls_in_dirs && isset( $urls['root_page'] )
&& ! empty( $urls['directory_for_default_language'] )
&& isset( $urls['show_on_root'] )
&& $urls['show_on_root'] === 'page'
&& $urls['root_page']
? $urls['root_page'] : false;
}
function wpml_home_url_init() {
global $pagenow, $sitepress;
if ( $pagenow == 'post.php' || $pagenow == 'post-new.php' ) {
$root_id = $this->get_root_page_id();
if ( ! empty( $_GET['wpml_root_page'] ) && ! empty( $root_id ) ) {
$rp = get_post( $root_id );
if ( $rp && $rp->post_status != 'trash' ) {
wp_redirect( get_edit_post_link( $root_id, 'no-display' ) );
exit;
}
}
if ( isset( $_GET['wpml_root_page'] ) && $_GET['wpml_root_page'] || ( isset( $_GET['post'] ) && $_GET['post'] == $root_id ) ) {
remove_action( 'admin_head', array( $sitepress, 'post_edit_language_options' ) );
add_action( 'admin_head', array( $this, 'wpml_home_url_language_box_setup' ) );
remove_action( 'page_link', array( $sitepress, 'permalink_filter' ), 1 );
}
}
}
function wpml_home_url_exclude_root_page_from_menus( $args ) {
if ( !empty( $args[ 'exclude' ] ) ) {
$args[ 'exclude' ] .= ',';
} else {
$args[ 'exclude' ] = '';
}
$args[ 'exclude' ] .= $this->get_root_page_id();
return $args;
}
/**
* Filters out all page menu items that point to the root page.
*
* @param object[] $items
*
* @return array
*
* @hook wp_get_nav_menu_items
*/
function exclude_root_page_menu_item( $items ) {
$root_id = $this->get_root_page_id();
foreach ( $items as $key => $item ) {
if ( isset( $item->object_id )
&& isset( $item->type )
&& $item->object_id == $root_id
&& $item->type === 'post_type'
) {
unset( $items[ $key ] );
}
}
return $items;
}
function wpml_home_url_exclude_root_page( $excludes ) {
$excludes[ ] = $this->get_root_page_id();
return $excludes;
}
function wpml_home_url_exclude_root_page2( $args ) {
$args[ 'exclude' ][ ] = $this->get_root_page_id();
return $args;
}
function wpml_home_url_get_pages( $pages ) {
$root_id = $this->get_root_page_id();
foreach ( $pages as $k => $page ) {
if ( $page->ID == $root_id ) {
unset( $pages[ $k ] );
$pages = array_values ( $pages );
break;
}
}
return $pages;
}
function wpml_home_url_language_box_setup() {
add_meta_box(
WPML_Meta_Boxes_Post_Edit_HTML::WRAPPER_ID,
__( 'Language', 'sitepress' ),
array( $this, 'wpml_home_url_language_box' ),
'page',
/**
* Filter meta box position.
*
* The context within the screen where the boxes should display. Available contexts vary from screen to screen.
* Post edit screen contexts include 'normal', 'side', and 'advanced'.
*
* @param String WPML_Meta_Boxes_Post_Edit_HTML::WRAPPER_ID Meta box ID.
*
* @since 4.2.8
*
*/
apply_filters( 'wpml_post_edit_meta_box_context', 'side', WPML_Meta_Boxes_Post_Edit_HTML::WRAPPER_ID ),
apply_filters( 'wpml_post_edit_meta_box_priority', 'high' )
);
}
function wpml_home_url_language_box( $post ) {
$root_id = $this->get_root_page_id();
if ( isset( $_GET[ 'wpml_root_page' ] )
|| ( !empty( $root_id )
&& $post->ID == $root_id ) ) {
_e ( "This page does not have a language since it's the site's root page." );
echo '<input type="hidden" name="_wpml_root_page" value="1" />';
}
}
function wpml_home_url_save_post_actions( $pidd, $post ) {
global $sitepress, $wpdb, $iclTranslationManagement;
if ( (bool) filter_input ( INPUT_POST, '_wpml_root_page' ) === true ) {
if ( isset( $_POST[ 'autosave' ] ) || ( isset( $post->post_type ) && $post->post_type == 'revision' ) ) {
return;
}
$iclsettings[ 'urls' ][ 'root_page' ] = $post->ID;
$sitepress->save_settings ( $iclsettings );
remove_action( 'save_post', array( $sitepress, 'save_post_actions' ), 10 );
if ( !is_null ( $iclTranslationManagement ) ) {
remove_action( 'save_post', array( $iclTranslationManagement, 'save_post_actions' ), 11 );
}
$update_args = array(
'element_id' => $post->ID,
'element_type' => 'post_page',
'context' => 'post'
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'before_delete' ) ) );
$wpdb->query (
$wpdb->prepare (
"DELETE FROM {$wpdb->prefix}icl_translations WHERE element_type='post_page' AND element_id=%d",
$post->ID
)
);
do_action( 'wpml_translation_update', array_merge( $update_args, array( 'type' => 'after_delete' ) ) );
}
}
function wpml_home_url_setup_root_page() {
global $sitepress, $wpml_query_filter;
remove_action( 'template_redirect', 'redirect_canonical' );
add_action( 'parse_query', array( $this, 'wpml_home_url_parse_query' ) );
remove_filter( 'posts_join', array( $wpml_query_filter, 'posts_join_filter' ), 10 );
remove_filter( 'posts_where', array( $wpml_query_filter, 'posts_where_filter' ), 10 );
$root_id = $this->get_root_page_id();
$rp = get_post( $root_id );
if ( $rp && $rp->post_status != 'trash' ) {
$sitepress->ROOT_URL_PAGE_ID = $root_id;
}
}
/**
* @param WP_Query $q
*
* @return mixed
*/
function wpml_home_url_parse_query( $q ) {
if ( ! $q->is_main_query() ) {
return $q;
}
if ( ! WPML_Root_Page::is_current_request_root() ) {
return $q;
} else {
remove_action( 'parse_query', array( $this, 'wpml_home_url_parse_query' ) );
$uri_path = trim( wpml_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ), '/' );
$uri_parts = explode( '/', $uri_path );
$potential_pagination_parameter = array_pop( $uri_parts );
$query_args = array();
wp_parse_str( wpml_parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ), $query_args );
if ( is_numeric( $potential_pagination_parameter ) ) {
$query_args['page'] = $potential_pagination_parameter;
}
$q->parse_query( $query_args );
$root_id = $this->get_root_page_id();
add_action( 'parse_query', array( $this, 'wpml_home_url_parse_query' ) );
if ( false !== $root_id ) {
$q = $this->set_page_query_parameters( $q, $root_id );
} else {
$front_page = get_option( 'page_on_front' );
if ( $front_page ) {
$q = $this->set_page_query_parameters( $q, $front_page );
}
}
}
return $q;
}
private function set_page_query_parameters( $q, $page_id ) {
$q->query_vars['page_id'] = $page_id;
$q->query['page_id'] = $page_id;
$q->is_page = 1;
$q->queried_object = new WP_Post( get_post( $page_id ) );
$q->queried_object_id = $page_id;
$q->query_vars['error'] = '';
$q->is_404 = false;
$q->query['error'] = null;
$q->is_home = false;
$q->is_singular = true;
return $q;
}
}
/**
* Checks if the language switcher is to be displayed.
* Used to check if the displayed page is a root page and the switcher is to be hidden because of it.
*
* @return bool true if the switcher is to be hidden
*/
function wpml_home_url_ls_hide_check() {
global $sitepress;
return $sitepress->get_setting( 'language_negotiation_type' ) == 1
&& (bool) ( $urls = $sitepress->get_setting( 'urls' ) ) === true
&& ! empty( $urls['directory_for_default_language'] )
&& isset( $urls['show_on_root'] ) && $urls['show_on_root'] === 'page'
&& ! empty( $urls['hide_language_switchers'] )
&& WPML_Root_Page::is_current_request_root();
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Class WPML_WordPress_Actions
* @package wpml-core
* @subpackage post-translation
*/
class WPML_WordPress_Actions {
/**
* @param int $post_id
*
* @return bool
*/
public static function is_bulk_trash( $post_id ) {
if ( self::is_trash_action() && self::post_id_in_bulk( $post_id ) ) {
return true;
} else {
return false;
}
}
/**
* @param int $post_id
*
* @return bool
*/
public static function is_bulk_untrash( $post_id ) {
if ( self::is_untrash_action() && self::post_id_in_bulk( $post_id, true ) ) {
return true;
} else {
return false;
}
}
public static function is_heartbeat( ) {
return self::is_action( 'heartbeat', 'post' );
}
protected static function is_trash_action() {
return self::is_action( 'trash' );
}
protected static function is_untrash_action() {
return self::is_action( 'untrash' );
}
/**
* @param string $action
*
* @return bool
*/
protected static function is_action( $action, $type = 'get' ) {
if ( $type == 'get' ) {
return ( isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] == $action ) || ( isset( $_GET[ 'action2' ] ) && $_GET[ 'action2' ] == $action );
} elseif ( $type == 'post' ) {
return ( isset( $_POST[ 'action' ] ) && $_POST[ 'action' ] == $action ) || ( isset( $_POST[ 'action2' ] ) && $_POST[ 'action2' ] == $action );
} else {
return false;
}
}
/**
* @param int $post_id
* @param bool $check_ids
*
* @return bool
*/
protected static function post_id_in_bulk( $post_id, $check_ids = false ) {
if ( isset( $_GET[ 'post' ] ) && is_array( $_GET[ 'post' ] ) && in_array( $post_id, $_GET[ 'post' ] ) ) {
return true;
} elseif ( $check_ids ) {
// We need to check the ids parameter when user clicks on 'undo' after trashing.
return isset( $_GET[ 'ids' ] ) && is_string( $_GET[ 'ids' ] ) && in_array( $post_id, explode( ',', $_GET[ 'ids' ] ) );
} else {
return false;
}
}
}