File: /var/www/indoadvisory/wp/wp-content/plugins/polylang-pro/modules/sync-post/sync-post.php
<?php
/**
* @package Polylang-Pro
*/
/**
* Manages the synchronization of posts across languages
*
* @since 2.1
*/
class PLL_Sync_Post {
/**
* @var PLL_Sync_Post_Model
*/
public $sync_model;
/**
* @var PLL_Model
*/
public $model;
/**
* Stores all synchronization buttons.
*
* @var PLL_Sync_Post_Button[]
*/
public $buttons;
/**
* Constructor
*
* @since 2.1
* @since 2.7 Registers twos option for the Translate bulk action.
*
* @param PLL_Frontend|PLL_Admin|PLL_Settings $polylang Polylang object.
*/
public function __construct( &$polylang ) {
$this->sync_model = &$polylang->sync_post_model;
$this->model = &$polylang->model;
// Create buttons in the language metabox.
if ( $polylang instanceof PLL_Admin && ! wp_doing_cron() && ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
// Loaded with `PLL_Admin_Loader`: we're on `admin_init` or `use_block_editor_for_post`.
$this->add_metabox_buttons();
}
add_action( 'pll_bulk_translate_options_init', array( $this, 'add_bulk_translate_options' ) );
add_filter( 'update_translation_group', array( $this, 'unsync_post' ), 10, 3 );
add_action( 'pll_save_post', array( $this, 'sync_posts' ), 5 ); // Before PLL_Admin_Sync, Before PLL_ACF, Before PLLWC.
}
/**
* Registers the buttons.
*
* @since 3.6.5
*
* @return void
*/
public function add_metabox_buttons(): void {
foreach ( $this->model->get_languages_list() as $language ) {
$this->buttons[ $language->slug ] = new PLL_Sync_Post_Button( $this->sync_model, $language );
}
}
/**
* Registers options of the Translate bulk action.
*
* @since 3.6.5
*
* @param PLL_Bulk_Translate $bulk_translate Instance of `PLL_Bulk_Translate`.
* @return void
*/
public function add_bulk_translate_options( $bulk_translate ): void {
$bulk_translate->register_options(
array(
new PLL_Sync_Post_Bulk_Option(
array(
'name' => 'pll_sync_post',
'description' => __( 'Synchronize translations in selected languages with the original items', 'polylang-pro' ),
'do_synchronize' => true,
'priority' => 10,
),
$this->model,
$this->sync_model
),
new PLL_Sync_Post_Bulk_Option(
array(
'name' => 'pll_copy_post',
'description' => __( 'Copy original items to selected languages', 'polylang-pro' ),
'do_synchronize' => false,
'priority' => 5,
),
$this->model,
$this->sync_model
),
)
);
}
/**
* Duplicates the post and saves the synchronization group
*
* @since 2.1
*
* @param int $post_id The post id.
* @return void
*/
public function sync_posts( $post_id ) {
static $avoid_recursion = false;
if ( $avoid_recursion ) {
return;
}
if ( ! empty( $_POST['post_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
// We are editing the post from post.php (only place where we can change the option to sync).
if ( ! empty( $_POST['pll_sync_post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
$sync_post = array_intersect( array_map( 'sanitize_key', $_POST['pll_sync_post'] ), array( 'true' ) ); // phpcs:ignore WordPress.Security.NonceVerification
}
if ( empty( $sync_post ) ) {
$this->sync_model->save_group( $post_id, array() );
return;
}
} else {
// Quick edit or bulk edit or any place where the Languages metabox is not displayed.
$sync_post = array_diff( $this->sync_model->get( $post_id ), array( $post_id ) ); // Just remove this post from the list.
}
$avoid_recursion = true;
$languages = array_keys( $sync_post );
foreach ( $languages as $k => $lang ) {
if ( $this->sync_model->current_user_can_synchronize( $post_id, $lang ) ) {
add_filter( 'is_sticky', array( $this, 'handle_sticky_post' ) );
$this->sync_model->copy_post( $post_id, $lang, false ); // Don't save the group inside the loop.
remove_filter( 'is_sticky', array( $this, 'handle_sticky_post' ) );
} else {
unset( $languages[ $k ] );
}
}
// Save group if the languages metabox is displayed.
if ( ! empty( $_POST['post_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
$this->sync_model->save_group( $post_id, $languages );
}
$avoid_recursion = false;
}
/**
*
* Unsynchronize a post from the post translations group.
*
* @since 3.2
*
* @param (int|string[])[] $tr List of translations with language codes as array keys and post IDs as array values.
* An optional element defined by the 'sync' key includes an array of synchronized translations
* with target language code as array keys and source language code as values.
* @param string $old_slug The old language slug.
* @param string $new_slug The new language slug.
* @return (int|string[])[]
*/
public function unsync_post( $tr, $old_slug, $new_slug ) {
if ( ! is_array( $tr ) || empty( $tr['sync'] ) || ! is_array( $tr['sync'] ) ) {
return $tr;
}
// Delete sync between translations when deleting a language or update slug.
// Search old slug in array keys.
if ( array_key_exists( $old_slug, $tr['sync'] ) ) {
if ( ! empty( $new_slug ) && ! empty( $tr['sync'][ $old_slug ] ) ) {
$tr['sync'][ $new_slug ] = $tr['sync'][ $old_slug ];
}
unset( $tr['sync'][ $old_slug ] );
}
// Search old slug in array values.
if ( in_array( $old_slug, $tr['sync'] ) ) {
foreach ( $tr['sync'] as $key => $value ) {
if ( $value !== $old_slug ) {
continue;
}
// If new slug then replace it.
if ( $new_slug ) {
$tr['sync'][ $key ] = $new_slug;
} else {
// Otherwise unset the old slug.
unset( $tr['sync'][ $key ] );
}
}
}
return $tr;
}
/**
* Some backward compatibility with Polylang < 2.6
* allows for example to call PLL()->sync_post->are_synchronized() used in Polylang for WooCommerce
*
* @since 2.6
*
* @param string $func Function name.
* @param array $args Function arguments.
* @return mixed|void
*/
public function __call( $func, $args ) {
if ( method_exists( $this->sync_model, $func ) ) {
if ( WP_DEBUG ) {
$debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
sprintf(
'%1$s() was called incorrectly in %2$s on line %3$s: the call to PLL()->sync_post->%1$s() has been deprecated in Polylang 2.6, use PLL()->sync_post->sync_model->%1$s() instead.' . "\nError handler",
esc_html( $func ),
esc_html( $debug[0]['file'] ),
absint( $debug[0]['line'] )
)
);
}
return call_user_func_array( array( $this->sync_model, $func ), $args );
}
$debug = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions
sprintf(
'Call to undefined function PLL()->sync_post->%1$s() in %2$s on line %3$s' . "\nError handler",
esc_html( $func ),
esc_html( $debug[0]['file'] ),
absint( $debug[0]['line'] )
),
E_USER_ERROR
);
}
/**
* Filter the sticky status of a post.
*
* @since 3.2
*
* @param bool $is_sticky Whether or not the source post is sticky.
* @return bool
*/
public function handle_sticky_post( $is_sticky ) {
if ( isset( $_POST['_inline_edit'], $_POST['action'] ) && wp_verify_nonce( $_POST['_inline_edit'], 'inlineeditnonce' ) && 'inline-save' === $_POST['action'] ) {
// For quick edit.
$is_sticky = isset( $_POST['sticky'] ) && 'sticky' === $_POST['sticky'];
} elseif ( isset( $_POST['_pll_nonce'], $_POST['action'] ) && wp_verify_nonce( $_POST['_pll_nonce'], 'pll_language' ) && 'editpost' === $_POST['action'] ) {
// For the classic editor.
$is_sticky = isset( $_POST['sticky'] ) && 'sticky' === $_POST['sticky'];
} elseif ( isset( $_GET['_wpnonce'], $_GET['action'], $_GET['sticky'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'bulk-posts' ) && 'edit' === $_GET['action'] ) {
// For bulk edit (note that 'sticky' can take 3 different values here).
switch ( $_GET['sticky'] ) {
case '-1':
break;
case 'unsticky':
$is_sticky = false;
break;
case 'sticky':
$is_sticky = true;
break;
}
}
return $is_sticky;
}
}