Add PSR HTTP Message Interfaces and Dependencies

- Implemented StreamInterface, UploadedFileInterface, and UriInterface as per PSR standards.
- Added getallheaders function to retrieve HTTP headers in a compatible manner.
- Included LICENSE files for ralouphie/getallheaders and symfony/deprecation-contracts.
- Introduced function for triggering deprecation notices in Symfony.
This commit is contained in:
2025-12-28 12:44:00 +01:00
parent cf600ae727
commit cd264483f8
410 changed files with 60841 additions and 16 deletions

View File

@@ -0,0 +1,618 @@
<?php
/**
* ATFPP Ajax Handler
*
* @package ATFPP
*/
/**
* Do not access the page directly
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use ATFPP\AI_Translate\Services\API\Enums\AI_Capability;
use ATFPP\AI_Translate\Services\API\Helpers;
/**
* Handle ATFPP ajax requests
*/
if ( ! class_exists( 'ATFPP_Ajax_Handler' ) ) {
class ATFPP_Ajax_Handler {
/**
* Member Variable
*
* @var instance
*/
private static $instance;
/**
* Stores custom block data for processing and retrieval.
*
* This static array holds the data related to custom blocks that are
* used within the plugin. It can be utilized to manage and manipulate
* the custom block information as needed during AJAX requests.
*
* @var array
*/
private $custom_block_data_array = array();
/**
* Gets an instance of our plugin.
*
* @param object $settings_obj timeline settings.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor.
*
* @param object $settings_obj Plugin settings.
*/
public function __construct() {
if ( is_admin() ) {
add_action( 'wp_ajax_atfp_fetch_post_content', array( $this, 'fetch_post_content' ) );
add_action('wp_ajax_atfp_fetch_post_meta_fields', array($this, 'fetch_post_meta_fields'));
add_action('wp_ajax_atfp_update_post_meta_fields', array($this, 'update_post_meta_fields'));
add_action( 'wp_ajax_atfp_block_parsing_rules', array( $this, 'block_parsing_rules' ) );
add_action( 'wp_ajax_atfp_get_custom_blocks_content', array( $this, 'get_custom_blocks_content' ) );
add_action( 'wp_ajax_atfp_update_custom_blocks_content', array( $this, 'update_custom_blocks_content' ) );
add_action('wp_ajax_atfp_update_translate_data', array($this, 'atfp_update_translate_data'));
add_action( 'wp_ajax_atfp_update_elementor_data', array( $this, 'update_elementor_data' ) );
add_action('wp_ajax_atfp_update_classic_translate_status', array($this, 'update_classic_translate_status'));
add_action('wp_ajax_atfp_update_custom_fields_content', array($this, 'update_custom_fields_content'));
}
}
/**
* Block Parsing Rules
*
* Handles the block parsing rules AJAX request.
*/
public function block_parsing_rules() {
if ( ! check_ajax_referer( 'atfp_translate_nonce', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if(!current_user_can('edit_posts')){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$block_parse_rules = ATFPP_Helper::get_instance()->get_block_parse_rules();
$data = array(
'blockRules' => json_encode( $block_parse_rules ),
);
return wp_send_json_success( $data );
exit;
}
/**
* Fetches post meta fields via AJAX request.
*/
public function fetch_post_meta_fields() {
if ( ! check_ajax_referer( 'atfp_fetch_post_meta_fields', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$post_id = isset( $_POST['postId']) ? absint(sanitize_text_field($_POST['postId'])) : false;
if(!isset($post_id) || false === $post_id){
wp_send_json_error( __( 'Invalid Post ID.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if(!current_user_can('edit_post', $post_id)){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$post_meta_sync = true;
if (!isset(PLL()->options['sync']) || (isset(PLL()->options['sync']) && !in_array('post_meta', PLL()->options['sync']))) {
$post_meta_sync = false;
}
if($post_meta_sync){
wp_send_json_success( __( 'Post meta sync is enabled. Please disable post meta sync in Polylang settings.', 'autopoly-ai-translation-for-polylang-pro' ) );
}
$allowed_meta_fields=ATFPP_Helper::get_allowed_custom_fields();
$post_meta_fields=get_post_meta($post_id);
$existed_meta_fields=array_intersect(array_keys($post_meta_fields), array_keys($allowed_meta_fields));
$filtered_meta_fields=array();
foreach($existed_meta_fields as $key){
if(isset($post_meta_fields[$key]) && !empty($post_meta_fields[$key]) && isset($allowed_meta_fields[$key]['status']) && true === $allowed_meta_fields[$key]['status']){
$value=$allowed_meta_fields[$key]['type'] && is_array($post_meta_fields[$key]) ? maybe_unserialize($post_meta_fields[$key][0]) : maybe_unserialize($post_meta_fields[$key]);
$filtered_meta_fields[$key]=$value;
}
}
wp_send_json_success( array( 'metaFields' => $filtered_meta_fields, 'allowedMetaFields' => $allowed_meta_fields ) );
exit;
}
public function update_post_meta_fields() {
if ( ! check_ajax_referer( 'atfp_update_post_meta_fields', 'post_meta_fields_key', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$post_id = isset( $_POST['post_id']) ? absint(sanitize_text_field($_POST['post_id'])) : false;
if(!isset($post_id) || false === $post_id){
wp_send_json_error( __( 'Invalid Post ID.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if(!current_user_can('edit_post', $post_id)){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$meta_fields = isset( $_POST['meta_fields']) ? json_decode(wp_unslash($_POST['meta_fields']), true) : false;
if(!$meta_fields || !is_array($meta_fields) || count($meta_fields) < 1){
wp_send_json_success( __( 'No Meta Fields to update.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 200 );
}
$this->update_post_custom_fields($meta_fields, $post_id);
wp_send_json_success( __( 'Meta Fields updated successfully.', 'autopoly-ai-translation-for-polylang-pro' ) );
exit;
}
/**
* Fetches post meta fields via AJAX request.
/**
* Fetches post content via AJAX request.
*/
public function fetch_post_content() {
if ( ! check_ajax_referer( 'atfp_translate_nonce', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$post_id = absint(isset( $_POST['postId'] ) ? absint(sanitize_text_field($_POST['postId'])) : false);
if(!current_user_can('edit_post', $post_id)){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
if ( false !== $post_id ) {
$post_data = get_post( absint($post_id) );
$locale = isset($_POST['local']) ? sanitize_text_field($_POST['local']) : 'en';
$current_locale = isset($_POST['current_local']) ? sanitize_text_field($_POST['current_local']) : 'en';
$slug_translation_option = get_option('atfp_slug_translation_option','title_translate');
$content = $post_data->post_content;
$content = ATFPP_Helper::replace_links_with_translations($content, $locale, $current_locale);
$data = array(
'title' => $post_data->post_title,
'excerpt' => $post_data->post_excerpt,
'content' => $content,
);
if($slug_translation_option === 'slug_translate' || $slug_translation_option === 'slug_keep'){
$data['slug_name']=urldecode(get_post_field('post_name', $post_id));
}
return wp_send_json_success( $data );
} else {
wp_send_json_error( __( 'Invalid Post ID.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
exit;
}
public function get_custom_blocks_content() {
if ( ! check_ajax_referer( 'atfp_block_update_nonce', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if(!current_user_can('edit_posts')){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$custom_content = get_option( 'atfp_custom_block_data', false ) ? get_option( 'atfp_custom_block_data', false ) : false;
if ( $custom_content && is_string( $custom_content ) && ! empty( trim( $custom_content ) ) ) {
return wp_send_json_success( array( 'block_data' => $custom_content ) );
} else {
return wp_send_json_success( array( 'message' => __( 'No custom blocks found.', 'autopoly-ai-translation-for-polylang-pro' ) ) );
}
exit();
}
public function update_custom_blocks_content() {
if ( ! check_ajax_referer( 'atfp_block_update_nonce', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if(!current_user_can('edit_posts')){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$json = isset($_POST['save_block_data']) ? wp_unslash($_POST['save_block_data']) : false;
$updated_blocks_data = json_decode($json, true);
if(json_last_error() !== JSON_ERROR_NONE){
wp_send_json_error( __( 'Invalid JSON', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if ( $updated_blocks_data ) {
$block_parse_rules = ATFPP_Helper::get_instance()->get_block_parse_rules();
if ( isset( $block_parse_rules['AtfpBlockParseRules'] ) ) {
$previous_translate_data = get_option( 'atfp_custom_block_translation', false );
if ( $previous_translate_data && ! empty( $previous_translate_data ) ) {
$this->custom_block_data_array = $previous_translate_data;
}
foreach ( $updated_blocks_data as $key => $block_data ) {
$this->verify_block_data( array( $key ), $block_data, $block_parse_rules['AtfpBlockParseRules'][ $key ] );
}
if ( count( $this->custom_block_data_array ) > 0 ) {
update_option( 'atfp_custom_block_translation', $this->custom_block_data_array );
}
delete_option( 'atfp_custom_block_data' );
}
}
return wp_send_json_success( array( 'message' => __( 'Automatic Translation for Polylang: Custom Blocks data updated successfully', 'autopoly-ai-translation-for-polylang-pro' ) ) );
}
private function verify_block_data( $id_keys, $value, $block_rules ) {
$block_rules = is_object( $block_rules ) ? json_decode( json_encode( $block_rules ) ) : $block_rules;
if ( ! isset( $block_rules ) ) {
return $this->create_nested_attribute( $value,$id_keys );
}
if ( is_array( $value ) && isset( $block_rules ) ) {
foreach ( $value as $key => $item ) {
if ( isset( $block_rules[ $key ] ) && is_array( $item ) ) {
$this->verify_block_data( array_merge( $id_keys, array( $key ) ), $item, $block_rules[ $key ], false );
continue;
} elseif ( ! isset( $block_rules[ $key ] ) && true === $item ) {
$this->create_nested_attribute( true,array_merge( $id_keys, array( $key ) ) );
continue;
} elseif ( ! isset( $block_rules[ $key ] ) && is_array( $item ) ) {
$this->create_nested_attribute( $item,array_merge( $id_keys, array( $key ) ) );
continue;
}
}
}
}
private function create_nested_attribute( $value,$id_keys = array() ) {
$value = is_object( $value ) ? json_decode( json_encode( $value ), true ) : $value;
$current_array = &$this->custom_block_data_array;
foreach ( $id_keys as $index => $id ) {
if ( ! isset( $current_array[ $id ] ) ) {
$current_array[ $id ] = array();
}
$current_array = &$current_array[ $id ];
}
$current_array = $value;
}
public function atfp_update_translate_data() {
if ( ! check_ajax_referer( 'atfp_translate_nonce', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$post_id = isset($_POST['post_id']) ? absint(sanitize_text_field($_POST['post_id'])) : 0;
$editor_type = isset($_POST['editorType']) ? sanitize_text_field($_POST['editorType']) : '';
$extra_data=isset($_POST['extraData']) ? json_decode(wp_unslash($_POST['extraData']), true) : [];
// Require capability based on context
if ( $post_id > 0 ) {
if ( ! current_user_can('edit_post', $post_id) && $editor_type !== 'taxonomy' ) {
wp_send_json_error( __( 'Unauthorized to edit post', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
if($editor_type === 'taxonomy'){
if ( ! current_user_can('edit_posts') ) {
wp_send_json_error( __( 'Unauthorized to edit terms', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
}
} else {
if ( ! current_user_can('edit_posts') ) {
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
}
$provider = isset($_POST['provider']) ? sanitize_text_field($_POST['provider']) : '';
$total_string_count = isset($_POST['totalStringCount']) ? absint($_POST['totalStringCount']) : 0;
$total_word_count = isset($_POST['totalWordCount']) ? absint($_POST['totalWordCount']) : 0;
$total_char_count = isset($_POST['totalCharacterCount']) ? absint($_POST['totalCharacterCount']) : 0;
$date = isset($_POST['date']) ? date('Y-m-d H:i:s', strtotime(sanitize_text_field($_POST['date']))) : '';
$source_string_count = isset($_POST['sourceStringCount']) ? absint($_POST['sourceStringCount']) : 0;
$source_word_count = isset($_POST['sourceWordCount']) ? absint($_POST['sourceWordCount']) : 0;
$source_char_count = isset($_POST['sourceCharacterCount']) ? absint($_POST['sourceCharacterCount']) : 0;
$source_lang = isset($_POST['sourceLang']) ? sanitize_text_field($_POST['sourceLang']) : '';
$target_lang = isset($_POST['targetLang']) ? sanitize_text_field($_POST['targetLang']) : '';
$time_taken = isset($_POST['timeTaken']) ? absint($_POST['timeTaken']) : 0;
if (class_exists('Atfpp_Dashboard')) {
$translation_data = array(
'post_id' => $post_id,
'service_provider' => $provider,
'source_language' => $source_lang,
'target_language' => $target_lang,
'time_taken' => $time_taken,
'string_count' => $total_string_count,
'word_count' => $total_word_count,
'character_count' => $total_char_count,
'source_string_count' => $source_string_count,
'source_word_count' => $source_word_count,
'source_character_count' => $source_char_count,
'editor_type' => $editor_type,
'date_time' => $date,
'version_type' => 'pro'
);
if(!empty($extra_data) && is_array($extra_data) && count($extra_data) > 0){
foreach($extra_data as $key => $value){
if(!isset($translation_data[$key]) && !empty($value) && !empty($key)){
$translation_data[sanitize_text_field($key)] = sanitize_text_field($value);
}
}
}
Atfpp_Dashboard::store_options(
'atfp',
'post_id',
'update',
$translation_data
);
wp_send_json_success(array(
'message' => __('Translation data updated successfully', 'autopoly-ai-translation-for-polylang-pro')
));
} else {
wp_send_json_error(array(
'message' => __('Atfpp_Dashboard class not found', 'autopoly-ai-translation-for-polylang-pro')
));
}
exit;
}
public function update_classic_translate_status() {
if ( ! check_ajax_referer( 'atfp_classic_translate_nonce', 'atfpp_update_translation_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$post_id = isset($_POST['post_id']) ? absint(sanitize_text_field($_POST['post_id'])) : 0;
if ( ! $post_id || ! current_user_can('edit_post', $post_id) ) {
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : '';
if ( $status !== 'completed' ) {
wp_send_json_error( __( 'Invalid status', 'autopoly-ai-translation-for-polylang-pro' ), 400 );
wp_die( '0', 400 );
}
update_post_meta($post_id, '_atfp_classic_translate_status', $status);
wp_send_json_success( 'Classic translate status updated.' );
}
/**
* Handle AJAX request to update Elementor data.
*/
public function update_elementor_data() {
if ( ! check_ajax_referer( 'atfp_translate_nonce', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$post_id = isset($_POST['post_id']) ? absint(sanitize_text_field($_POST['post_id'])) : 0;
if ( ! $post_id || ! current_user_can('edit_post', $post_id) ) {
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
// Optional hardening: enforce valid JSON if not using Elementor Document API
if ( isset($_POST['elementor_data']) && is_string($_POST['elementor_data']) ) {
$decoded = json_decode( stripslashes( $_POST['elementor_data'] ), true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
wp_send_json_error( __( 'Invalid data.', 'autopoly-ai-translation-for-polylang-pro' ), 400 );
wp_die( '0', 400 );
}
}
$parent_post_id=intval( $_POST['parent_post_id'] );
$slug_translation_option = get_option('atfp_slug_translation_option','title_translate');
$current_slug=get_post_field('post_name', $post_id);
$new_post_name=false;
$new_meta_fields=isset($_POST['meta_fields']) ? json_decode(wp_unslash($_POST['meta_fields']), true) : false;
$elementor_data = isset($_POST['elementor_data']) ? sanitize_text_field(wp_unslash($_POST['elementor_data'])) : '';
if('' === $current_slug){
if(isset($_POST['post_name']) && '' !== $_POST['post_name'] && $slug_translation_option === 'slug_translate'){
$new_post_name=sanitize_title($_POST['post_name']);
}else if($slug_translation_option === 'slug_keep'){
$new_post_name=sanitize_text_field(get_post_field('post_name', $parent_post_id));
}
}
// Check if the current post has Elementor data
if($elementor_data && '' !== $elementor_data){
if(class_exists('Elementor\Plugin')){
$plugin=\Elementor\Plugin::$instance;
$document=$plugin->documents->get($post_id);
$elementor_data=json_decode(wp_unslash($_POST['elementor_data']), true);
if (json_last_error() !== JSON_ERROR_NONE) {
wp_send_json_error( __( 'Invalid Elementor data.', 'autopoly-ai-translation-for-polylang-pro' ), 400 );
wp_die( '0', 400 );
}
$document->save( [
'elements' => $elementor_data,
] );
$plugin->files_manager->clear_cache();
$this->update_post_custom_fields($new_meta_fields, $post_id);
update_post_meta($post_id, '_atfp_elementor_translated', 'true');
}
}
if($new_post_name && '' !== $new_post_name){
wp_update_post(array(
'ID' => $post_id,
'post_name' => $new_post_name
));
}
wp_send_json_success( 'Elementor data updated.' );
exit;
}
private function update_post_custom_fields($fields, $post_id){
$post_meta_sync = true;
if (!isset(PLL()->options['sync']) || (isset(PLL()->options['sync']) && !in_array('post_meta', PLL()->options['sync']))) {
$post_meta_sync = false;
}
if($post_meta_sync){
return;
}
$allowed_meta_fields=ATFPP_Helper::get_allowed_custom_fields();
if($fields && is_array($fields) && count($fields) > 0){
$valid_meta_fields=array_intersect(array_keys($fields), array_keys($allowed_meta_fields));
if(count($valid_meta_fields) > 0){
foreach($valid_meta_fields as $key){
if(isset($allowed_meta_fields[$key]) && $allowed_meta_fields[$key]['status']){
$value=is_array($fields[$key]) ? $this->sanitize_array_value($fields[$key], array()) : sanitize_text_field($fields[$key]);
update_post_meta(absint($post_id), sanitize_text_field($key), $value);
}
}
}
}
}
private function sanitize_array_value($value, $arr){
foreach($value as $key => $item){
$arr[sanitize_text_field($key)]=is_array($item) ? $this->sanitize_array_value($item, array()) : sanitize_text_field($item);
}
return $arr;
}
public function update_custom_fields_content() {
if ( ! check_ajax_referer( 'atfp_save_custom_fields', 'atfp_nonce', false ) ) {
wp_send_json_error( __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
if(!current_user_can('edit_posts')){
wp_send_json_error( __( 'Unauthorized', 'autopoly-ai-translation-for-polylang-pro' ), 403 );
wp_die( '0', 403 );
}
$json = isset($_POST['save_custom_fields_data']) ? wp_unslash($_POST['save_custom_fields_data']) : false;
$updated_custom_fields_data = json_decode($json, true);
$updated_custom_fields_data=array_map('sanitize_text_field', $updated_custom_fields_data);
$existing_fields=get_option('atfp_allowed_custom_fields', false);
if(json_last_error() !== JSON_ERROR_NONE){
wp_send_json_error( __( 'Invalid JSON', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$allowed_fields=ATFPP_Helper::get_instance()->get_custom_fields_data();
if(!$allowed_fields || !is_array($allowed_fields)){
wp_send_json_error( __( 'Invalid allowed fields', 'autopoly-ai-translation-for-polylang-pro' ) );
wp_die( '0', 400 );
}
$allowed_fields_values=array_keys($allowed_fields);
$valid_fields=array_intersect($updated_custom_fields_data, $allowed_fields_values);
$sanitize_fields=array();
$old_fields=array();
foreach($valid_fields as $field){
if(!isset($existing_fields[$field]) || $existing_fields[$field]['status'] !== true || $existing_fields[$field]['type'] !== $allowed_fields[$field]['type']){
$sanitize_fields[sanitize_text_field($field)]=['status'=>true, 'type'=>sanitize_text_field($allowed_fields[$field]['type'])];
}else{
$old_fields[$field]=$existing_fields[$field];
}
}
$unset_fields=array_diff(array_keys($existing_fields), $updated_custom_fields_data );
foreach($unset_fields as $field){
if(isset($existing_fields[$field]) && $existing_fields[$field]['status'] === true){
$sanitize_fields[$field]=$existing_fields[$field];
$sanitize_fields[$field]['status']=false;
}
}
if(count($sanitize_fields) < 1){
wp_send_json_success(array( 'message' => __( 'No changes detected. All selected custom fields are already up to date.', 'autopoly-ai-translation-for-polylang-pro' ) ));
exit;
}
update_option('atfp_allowed_custom_fields', array_merge($old_fields, $sanitize_fields));
$save_settings=get_option('atfp_allowed_custom_fields', false);
if ( ! $save_settings || ! is_array( $save_settings ) || count( $save_settings ) < 1 ) {
wp_send_json_success( array( 'message' => __( 'No custom fields selected. Autopoly cannot translate any fields.', 'autopoly-ai-translation-for-polylang-pro' ) ) );
exit;
}
wp_send_json_success( array(
'message' => __( 'Custom fields translation settings have been updated successfully. Your selected fields will now be translated automatically.', 'autopoly-ai-translation-for-polylang-pro' ),
'updated_fields' => $sanitize_fields
) );
exit;
}
}
}

View File

@@ -0,0 +1,631 @@
<?php
/**
* ATFPP Ajax Handler
*
* @package ATFPP
*/
/**
* Do not access the page directly
*/
if (! defined('ABSPATH')) {
exit;
}
/**
* ATFPP Helper
*/
if (! class_exists('ATFPP_Helper')) {
class ATFPP_Helper
{
/**
* Member Variable
*
* @var instance
*/
private static $instance;
/**
* Stores custom block data for processing and retrieval.
*
* This static array holds the data related to custom blocks that are
* used within the plugin. It can be utilized to manage and manipulate
* the custom block information as needed during AJAX requests.
*
* @var array
*/
private $custom_block_data_array = array();
/**
* Gets an instance of our plugin.
*
* @param object $settings_obj timeline settings.
*/
public static function get_instance()
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public static function get_custom_block_post_id()
{
$first_post_id = null;
$query = new WP_Query(
array(
'post_type' => 'atfp_add_blocks',
'posts_per_page' => 1,
'orderby' => 'date',
'order' => 'ASC',
)
);
$existing_post = $query->posts ? $query->posts[0] : null;
if (! $existing_post) {
$post_title = esc_html__('Add More Gutenberg Blocks', 'automatic-translation-for-polylang-pro');
$first_post_id = wp_insert_post(
array(
'post_title' => $post_title,
'post_content' => '',
'post_status' => 'publish',
'post_type' => 'atfp_add_blocks',
)
);
} elseif ($query->have_posts()) {
$query->the_post();
$first_post_id = get_the_ID();
}
return $first_post_id;
}
public function get_block_parse_rules()
{
$response = wp_remote_get( esc_url_raw( ATFPP_URL . 'includes/block-translation-rules/block-rules.json' ), array(
'timeout' => 15,
) );
if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
global $wp_filesystem;
// Initialize the WordPress filesystem
if ( ! function_exists( 'WP_Filesystem' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
WP_Filesystem();
$local_path = ATFPP_DIR_PATH . 'includes/block-translation-rules/block-rules.json';
if($wp_filesystem->exists($local_path) && $wp_filesystem->is_readable( $local_path )){
$block_rules = $wp_filesystem->get_contents( $local_path );
}else{
$block_rules = array();
}
} else {
$block_rules = wp_remote_retrieve_body( $response );
}
if(empty($block_rules)){
return array();
}
$block_translation_rules = json_decode($block_rules, true);
$this->custom_block_data_array = isset($block_translation_rules['AtfpBlockParseRules']) ? $block_translation_rules['AtfpBlockParseRules'] : null;
$custom_block_translation = get_option('atfp_custom_block_translation', false);
if (! empty($custom_block_translation) && is_array($custom_block_translation)) {
foreach ($custom_block_translation as $key => $block_data) {
$block_rules = isset($block_translation_rules['AtfpBlockParseRules'][$key]) ? $block_translation_rules['AtfpBlockParseRules'][$key] : null;
$this->filter_custom_block_rules(array($key), $block_data, $block_rules);
}
}
$block_translation_rules['AtfpBlockParseRules'] = $this->custom_block_data_array ? $this->custom_block_data_array : array();
return $block_translation_rules;
}
private function filter_custom_block_rules(array $id_keys, $value, $block_rules, $attr_key = false)
{
$block_rules = is_object($block_rules) ? json_decode(json_encode($block_rules)) : $block_rules;
if (! isset($block_rules)) {
return $this->merge_nested_attribute($id_keys, $value);
}
if (is_object($value) && isset($block_rules)) {
foreach ($value as $key => $item) {
if (isset($block_rules[$key]) && is_object($item)) {
$this->filter_custom_block_rules(array_merge($id_keys, array($key)), $item, $block_rules[$key], false);
continue;
} elseif (! isset($block_rules[$key]) && true === $item) {
$this->merge_nested_attribute(array_merge($id_keys, array($key)), true);
continue;
} elseif (! isset($block_rules[$key]) && is_object($item)) {
$this->merge_nested_attribute(array_merge($id_keys, array($key)), $item);
continue;
}
}
}
}
private function merge_nested_attribute(array $id_keys, $value)
{
$value = is_object($value) ? json_decode(json_encode($value), true) : $value;
$current_array = &$this->custom_block_data_array;
foreach ($id_keys as $index => $id) {
if (! isset($current_array[$id])) {
$current_array[$id] = array();
}
$current_array = &$current_array[$id];
}
$current_array = $value;
}
public static function replace_links_with_translations($content, $locale, $current_locale)
{
// Get all URLs in the content that start with the current home page URL (current domain), regardless of attribute or tag
$home_url = preg_quote(get_home_url(), '/');
$pattern = '/(' . $home_url . '[^\s"\'<>]*)/i';
$terms_data=self::get_terms_data();
if (preg_match_all($pattern, $content, $matches)) {
$image_urls=self::get_image_ids_by_urls($matches[1], $locale);
$content = preg_replace_callback($pattern, function ($match) use ($image_urls, $locale, $current_locale, $terms_data) {
$href = $match[1];
if (preg_match('/<img[^>]+(src|srcset)=[\'"][^\'"]*' . preg_quote($href, '/') . '[\'"][^>]*>/i', $match[0])) {
return $href;
}
if (isset($image_urls[$href]) && !empty($image_urls[$href])) {
return $image_urls[$href];
}
$postID = url_to_postid($href);
if ($postID > 0) {
$translatedPost = pll_get_post($postID, $locale);
if ($translatedPost) {
$link = get_permalink($translatedPost);
if ($link) {
return esc_url(urldecode_deep($link));
}
}
}
$path = trim(str_replace(pll_home_url($current_locale), '', $href), '/');
$category_slug = end(array_filter(explode('/', $path)));
$taxonomy_name = self::extract_taxonomy_name($path, $terms_data);
$taxonomy_name = $taxonomy_name ? $taxonomy_name : 'category';
$category = get_term_by('slug', $category_slug, $taxonomy_name);
if (!$category) {
// Remove the language prefix if using Polylang
$languages = pll_languages_list(); // e.g., ['en', 'fr']
$segments = explode('/', $path);
if (in_array($segments[0], $languages)) {
$lang_code = $segments[0];
$category_id = Pll()->model->term_exists_by_slug($category_slug, $lang_code, $taxonomy_name);
if ($category_id) {
$category = get_term($category_id, $taxonomy_name);
}
}
}
if ($category) {
$term_id = pll_get_term($category->term_id, $locale);
if ($term_id > 0) {
$link = get_category_link($term_id);
return esc_url($link);
}
}
return $href;
}, $content);
}
return $content;
}
private static function extract_taxonomy_name($path, $terms_data){
// Remove the language prefix if using Polylang
$languages = pll_languages_list(); // e.g., ['en', 'fr']
$segments = explode('/', $path);
if (in_array($segments[0], $languages)) {
array_shift($segments); // remove 'en', 'fr', etc.
}
if (empty($segments)) {
return null;
}
// First segment after language is usually the taxonomy slug
$possible_tax = $segments[0];
if (taxonomy_exists($possible_tax) || (isset($terms_data[$possible_tax]) && taxonomy_exists($terms_data[$possible_tax]))) {
return isset($terms_data[$possible_tax]) ? $terms_data[$possible_tax] : $possible_tax;
}
return false;
}
private static function get_terms_data(){
$taxonomies=get_taxonomies([],'objects');
$taxonomies_data=array();
foreach($taxonomies as $key=>$taxonomy){
if(isset($taxonomy->rewrite['slug'])){
$taxonomies_data[$taxonomy->rewrite['slug']]=$key;
}else{
$taxonomies_data[$key]=$key;
}
}
return $taxonomies_data;
}
private static function get_image_ids_by_urls( $image_urls = [] , $locale='en') {
global $wpdb;
// Convert single URL string to array
if ( is_string( $image_urls ) ) {
$image_urls = [ $image_urls ];
}
if ( empty( $image_urls ) || ! is_array( $image_urls ) ) {
return [];
}
$upload_dir = wp_upload_dir();
$base_url = $upload_dir['baseurl'];
$results = [];
$cleaned_paths_map = []; // [cleaned_path] => [original_urls]
foreach ( $image_urls as $url ) {
if ( strpos( $url, $base_url ) === false ) {
$results[ $url ] = false;
continue;
}
// Relative path
$relative_path = str_replace( $base_url . '/', '', $url );
// Strip size suffix if present
$cleaned_path = preg_replace( '/-\d+x\d+(?=\.(jpg|jpeg|png|gif|webp)$)/i', '', $relative_path );
// Map cleaned path to original URL(s)
if ( ! isset( $cleaned_paths_map[ $cleaned_path ] ) ) {
$cleaned_paths_map[ $cleaned_path ] = [];
}
$cleaned_paths_map[ $cleaned_path ][] = $url;
}
$like_parts = [];
$args = [];
// Build the OR'ed LIKEs and gather args
foreach ( array_keys( $cleaned_paths_map ) as $path ) {
$like_parts[] = 'guid LIKE %s';
// Always escape for LIKE, then add wildcards yourself
$args[] = '%' . $wpdb->esc_like( $path ) . '%';
}
// Nothing to search? bail early.
if ( empty( $like_parts ) ) {
return $results;
}
// Compose the SQL with placeholders only
$sql = "
SELECT ID, guid
FROM {$wpdb->posts}
WHERE post_type = %s
AND (" . implode( ' OR ', $like_parts ) . ")
";
// First arg is for post_type, then all the LIKE args
array_unshift( $args, 'attachment' );
// Prepare once with all arguments (wpdb::prepare accepts an array)
$prepared = $wpdb->prepare( $sql, $args );
// Run the query
$found = $wpdb->get_results( $prepared );
// Map found GUIDs to IDs
$guid_to_id = [];
foreach ( $found as $row ) {
$guid_to_id[ $row->guid ] = esc_url($row->guid);
$translated_post=pll_get_post(intval($row->ID), $locale);
if($translated_post){
$image_url=wp_get_attachment_url(intval($translated_post));
if($image_url && !empty($image_url)){
$guid_to_id[$row->guid]=esc_url($image_url);
}
}
}
// Match original URLs to found IDs
foreach ( $image_urls as $original_url ) {
$found_id = false;
if(isset($guid_to_id[$original_url])){
$results[$original_url]=$guid_to_id[$original_url];
continue;
}
// Exact match first
if ( isset( $guid_to_id[ $original_url ] ) ) {
$found_id = $guid_to_id[ $original_url ];
} else {
// Fallback to cleaned path match
$relative_path = str_replace( $base_url . '/', '', $original_url );
$cleaned_path = preg_replace('/-\d+x\d+(?=\.(jpg|jpeg|png|gif|webp)$)/i', '', $relative_path );
preg_match('/-\d+x\d+(?=\.(jpg|jpeg|png|gif|webp)$)/i', $original_url, $matches);
$suffix = isset($matches[0]) ? $matches[0] : '';
foreach ( $guid_to_id as $guid => $url ) {
if ( strpos( $guid, $cleaned_path ) !== false ) {
if (!empty($suffix)) {
// Insert $suffix before the file extension in $url
$found_id = preg_replace('/(\.[a-zA-Z0-9]+)$/', $suffix . '$1', $url);
} else {
$found_id = $url;
}
break;
}
}
}
$results[ $original_url ] = esc_url($found_id);
}
return $results;
}
public static function translation_data_migration(){
$already_migrated = get_option('atfp_translation_string_migration', false);
if(!$already_migrated){
$old_data=get_option('cpt_dashboard_data', array());
$updated=array();
if(isset($old_data['atfp']) && count($old_data['atfp']) > 0){
foreach($old_data['atfp'] as $data){
$updated_data=$data;
if(isset($data['string_count'])){
$updated_data['word_count']=$data['string_count'];
$updated_data['string_count']=$data['string_count'] / 30;
}
if(isset($data['source_string_count'])){
$updated_data['source_word_count']=$data['source_string_count'];
$updated_data['source_string_count']=$data['source_string_count'] / 30;
}
$updated[]=$updated_data;
}
if(count($updated) > 0){
$old_data['atfp']=$updated;
update_option('cpt_dashboard_data', $old_data);
}
}
update_option('atfp_translation_string_migration', true);
}
}
public static function get_custom_fields_data(){
$result=self::get_custom_fields_query();
$data=array();
if($result && is_array($result)){
$excluded_fields=self::get_excluded_custom_fields_keys();
$allowed_fields=self::get_allowed_custom_fields();
foreach($result as $result){
if(in_array($result['meta_key'], $excluded_fields)){
continue;
}
$serialized_value=maybe_unserialize($result['meta_value']);
$value_type=json_decode($result['meta_value'], true) ? 'array' : (is_array($serialized_value) ? 'array' : 'string');
$type=isset($allowed_fields[$result['meta_key']]) && true === $allowed_fields[$result['meta_key']]['status'] ? $allowed_fields[$result['meta_key']]['type'] : $value_type;
$status=isset($allowed_fields[$result['meta_key']]) && true === $allowed_fields[$result['meta_key']]['status'] ? 'Supported' : 'Unsupported';
$data[sanitize_text_field($result['meta_key'])]=['type'=>$type, 'status'=>$status];
}
}
$default_allowed_fields=self::get_default_allowed_fields();
$default_key_diff=array_diff(array_keys($default_allowed_fields), array_keys($data));
foreach($default_key_diff as $key){
$status='Supported';
$saved_allowed_fields=get_option('atfp_allowed_custom_fields', false);
$status=isset($saved_allowed_fields[$key]) && true === $saved_allowed_fields[$key]['status'] ? 'Supported' : 'Unsupported';
$data[$key]=['type'=>$default_allowed_fields[$key]['type'], 'status'=>$status];
}
$data=apply_filters('atfp/custom_fields/all_fields', $data);
return $data;
}
private static function get_custom_fields_query(){
global $wpdb;
// Escape LIKE pattern for system meta (_%)
$like_pattern = $wpdb->esc_like('_') . '%';
// SQL with DISTINCT + filtering
$sql = $wpdb->prepare(
"
SELECT DISTINCT pm.meta_key, pm.meta_value
FROM {$wpdb->postmeta} pm
WHERE pm.meta_key NOT LIKE %s
AND pm.meta_value <> '' -- skip empty
AND pm.meta_value NOT IN ('0','1') -- skip boolean
AND pm.meta_value NOT REGEXP '^[0-9]+$' -- skip integer
AND pm.meta_value NOT REGEXP '^[0-9]+\\.[0-9]+$' -- skip decimal
AND pm.meta_value NOT REGEXP '^(https?:\/\/|www\.)[A-Za-z0-9\.\-]+.*$' -- skip URLs
ORDER BY pm.meta_key ASC
",
$like_pattern
);
// Get results
$results = $wpdb->get_results($sql, ARRAY_A);
return $results;
}
private static function get_excluded_custom_fields_keys(){
$excluded_fields= array(
'_edit_last',
'_edit_lock',
'_wp_page_template',
'_wp_attachment_metadata',
'_icl_translator_note',
'_alp_processed',
'_pingme',
'_encloseme',
'_icl_lang_duplicate_of',
'atfpp_parent_post_language',
'atfp_parent_post_language_slug',
'atfpp_parent_post_language_slug',
'twae_exists',
'twae_post_migration',
'twae_style_migration',
'_thumbnail_id',
);
return apply_filters('atfp/custom_fields/excluded_keys', $excluded_fields);
}
public static function get_allowed_custom_fields(){
$allowed_custom_fields=self::get_allowed_custom_fields_data();
$allowed_custom_fields=apply_filters('atfp/custom_fields/allowed_fields', $allowed_custom_fields);
return $allowed_custom_fields;
}
private static function get_allowed_custom_fields_data(){
$allowed_fields=get_option('atfp_allowed_custom_fields', false);
if($allowed_fields && is_array($allowed_fields) && count($allowed_fields) > 0){
return $allowed_fields;
}
if(!$allowed_fields || !is_array($allowed_fields)){
$default_allowed_fields=self::get_default_allowed_fields();
foreach($default_allowed_fields as $key => $value){
$allowed_fields[$key]=['status'=>true, 'type'=>'string'];
}
update_option('atfp_allowed_custom_fields', $allowed_fields);
}
ksort($allowed_fields);
return $allowed_fields;
}
private static function get_default_allowed_fields(){
$found=false;
$response = wp_remote_get( esc_url_raw( ATFPP_URL . 'includes/block-translation-rules/default-allow-metafields.json' ), array(
'timeout' => 15,
) );
if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
global $wp_filesystem;
// Initialize the WordPress filesystem
if ( ! function_exists( 'WP_Filesystem' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
WP_Filesystem();
$local_path = ATFPP_DIR_PATH . 'includes/block-translation-rules/default-allow-metafields.json';
if($wp_filesystem->exists($local_path) && $wp_filesystem->is_readable( $local_path )){
$found=true;
$default_allowed_fields = $wp_filesystem->get_contents( $local_path );
}
}else{
$found=true;
$default_allowed_fields = wp_remote_retrieve_body( $response );
}
if(!$found){
return array();
}
return json_decode($default_allowed_fields, true) ;
}
public static function bulk_translation_render($current_screen){
global $polylang;
if(!$polylang || !property_exists($polylang, 'model')){
return false;
}
$translated_post_types = $polylang->model->get_translated_post_types();
$translated_taxonomies = $polylang->model->get_translated_taxonomies();
$translated_post_types = array_values($translated_post_types);
$translated_taxonomies = array_values($translated_taxonomies);
$translated_post_types=array_filter($translated_post_types, function($post_type){
return is_string($post_type);
});
$translated_taxonomies=array_filter($translated_taxonomies, function($taxonomy){
return is_string($taxonomy);
});
$valid_post_type=(isset($current_screen->post_type) && !empty($current_screen->post_type)) && in_array($current_screen->post_type, $translated_post_types) && $current_screen->post_type !== 'attachment' ? $current_screen->post_type : false;
$valid_taxonomy=(isset($current_screen->taxonomy) && !empty($current_screen->taxonomy)) && in_array($current_screen->taxonomy, $translated_taxonomies) ? $current_screen->taxonomy : false;
if((!$valid_post_type && !$valid_taxonomy) || ((!$valid_post_type || empty($valid_post_type)) && !isset($valid_taxonomy)) || (isset($current_screen->taxonomy) && !empty($current_screen->taxonomy) && !$valid_taxonomy)){
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,987 @@
<?php
if (!defined('ABSPATH')) exit;
use ATFPP\AI_Translate\Services\API\Enums\AI_Capability;
use ATFPP\AI_Translate\Services\API\Helpers;
if (!class_exists('ATFP_Register_Route')):
/**
* ATFP_Register_Route
*
* @package ATFPP\AI_Translate\Services\API\Helpers
*/
class ATFP_Register_Route
{
/**
* The base name of the route.
*
* @var string
*/
private $base_name;
/**
* Constructor
*
* @param string $base_name The base name of the route.
*/
public function __construct($base_name)
{
$this->base_name = $base_name;
add_action('rest_api_init', array($this, 'register_routes'));
}
/**
* Register the routes
*/
public function register_routes()
{
register_rest_route($this->base_name, '/(?P<slug>[\w-]+):translate-text', array(
'methods' => 'POST',
'callback' => array($this, 'ai_translation'),
'permission_callback' => array($this, 'permission_only_admins'),
'args' => array(
'slug' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_key',
),
'atfp_nonce' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => array($this, 'validate_atfp_ai_translate_nonce'),
),
'strings' => array(
'type' => 'string',
'required' => true,
),
'target_language' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
),
));
register_rest_route($this->base_name, '/(?P<slug>[\w-]+):bulk-translate-entries', array(
'methods' => 'POST',
'callback' => array($this, 'bulk_translate_entries'),
'permission_callback' => array($this, 'permission_only_admins'),
'args' => array(
'ids' => array(
'type' => 'string',
'required' => true,
),
'lang' => array(
'type' => 'string',
'required' => true,
),
'privateKey' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => array($this, 'validate_atfpp_bulk_nonce'),
),
),
));
register_rest_route($this->base_name, '/(?P<slug>[\w-]+):bulk-translate-taxonomy-entries', array(
'methods' => 'POST',
'callback' => array($this, 'bulk_translate_taxonomy_entries'),
'permission_callback' => array($this, 'permission_only_admins'),
'args' => array(
'taxonomy' => array(
'type' => 'string',
'required' => true,
),
'lang' => array(
'type' => 'string',
'required' => true,
),
'privateKey' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => array($this, 'validate_atfpp_bulk_nonce'),
),
'ids' => array(
'type' => 'string',
'required' => true,
),
),
));
register_rest_route($this->base_name, '/(?P<post_id>[\w-]+):create-translate-post', array(
'methods' => 'POST',
'callback' => array($this, 'create_translate_post'),
'permission_callback' => array($this, 'permission_only_admins'),
'args' => array(
'privateKey' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => array($this, 'validate_atfpp_create_post_nonce'),
),
'post_id' => array(
'type' => 'integer',
'required' => true,
'sanitize_callback' => 'absint',
),
'target_language' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
'editor_type' => array(
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
),
'source_language' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
'post_title' => array(
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
),
'post_content' => array(
'type' => 'string',
'required' => false,
),
),
));
register_rest_route($this->base_name, '/(?P<term_id>[\w-]+):create-translate-taxonomy', array(
'methods' => 'POST',
'callback' => array($this, 'create_translate_taxonomy'),
'permission_callback' => array($this, 'permission_only_admins'),
'args' => array(
'term_id' => array(
'required' => true,
'type' => 'integer',
'required' => true,
'sanitize_callback' => 'absint',
),
'privateKey' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => array($this, 'validate_atfpp_create_term_nonce'),
),
'target_language' => array(
'required' => true,
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
'source_language' => array(
'required' => true,
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
'taxonomy' => array(
'required' => true,
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
'taxonomy_name' => array(
'required' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
'taxonomy_slug' => array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
'taxonomy_description' => array(
'required' => true,
'type' => 'string',
'sanitize_callback' => 'wp_kses_post',
),
),
));
}
public function permission_only_admins( $request ){
$nonce = $request->get_header( 'X-WP-Nonce' );
if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
return new WP_Error( 'rest_forbidden', __( 'Invalid nonce.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 403 ) );
}
if ( ! is_user_logged_in() ) {
return new \WP_Error( 'rest_forbidden', __( 'You are not authorized to perform this action.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 401 ) );
}
if ( ! current_user_can( 'edit_posts' ) ) {
return new \WP_Error( 'rest_forbidden', __( 'You are not authorized to perform this action.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 403 ) );
}
return true;
}
public function validate_atfp_ai_translate_nonce( $value, $request, $param ){
return wp_verify_nonce( $value, 'atfp_ai_translate_nonce' ) ? true : new \WP_Error( 'rest_invalid_param', __( 'Invalid security token sent.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 403 ) );
}
public function validate_atfpp_bulk_nonce( $value, $request, $param ){
return wp_verify_nonce( $value, 'atfpp_bulk_translate_entries_nonce' ) ? true : new \WP_Error( 'rest_invalid_param', __( 'You are not authorized to perform this action.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 403 ) );
}
public function validate_atfpp_create_post_nonce( $value, $request, $param ){
return wp_verify_nonce( $value, 'atfpp_create_translate_post_nonce' ) ? true : new \WP_Error( 'rest_invalid_param', __( 'You are not authorized to perform this action.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 403 ) );
}
public function validate_atfpp_create_term_nonce( $value, $request, $param ){
return wp_verify_nonce( $value, 'atfpp_create_translate_taxonomy_nonce' ) ? true : new \WP_Error( 'rest_invalid_param', __( 'You are not authorized to perform this action.', 'autopoly-ai-translation-for-polylang-pro' ), array( 'status' => 403 ) );
}
/**
* AI Translation
*
* @param WP_REST_Request $params The request parameters.
* @return WP_REST_Response The response.
*/
public function ai_translation($params)
{
// Check if the user is logged in and has the necessary capabilities
if (!is_user_logged_in()) wp_send_json_error('You are not authorized to perform this action.');
if (!current_user_can('edit_posts')) wp_send_json_error('You are not authorized to perform this action.');
// Get the parameters from the request
$params = $params->get_params();
// Get the service slug
$service_slug = $params['slug'];
// Verify the nonce
if (!wp_verify_nonce($params['atfp_nonce'], 'atfp_ai_translate_nonce')) wp_send_json_error('You are not authorized to perform this action.');
// Check if the user has the necessary capabilities
if (wp_get_current_user()->has_cap('edit_posts')) {
// Check if the service is available
if (atfpp_ai_services()->is_service_available($params['slug'])) {
// Get the selected model
$selected_model = '';
if ( $params['slug'] === 'google' ) {
$selected_model = get_option('atfpp_selected_google_model', '');
} elseif ( $params['slug'] === 'openai' ) {
$selected_model = get_option('atfpp_selected_openai_model', '');
}
// Get the service
$service = atfpp_ai_services()->get_available_service($params['slug']);
// Get the strings
$strings = $params['strings'];
// Convert strings to array if it's a JSON string
$string_array = is_string($strings) ? json_decode($strings, true) : $strings;
if(strpos($strings, '&lt;') !== false && strpos($strings, '&gt;') !== false){
$strings = html_entity_decode($strings);
}
// Get the target language
$target_language = $params['target_language'];
// Use the source language from params
$source_language = $params['source_language'];
$glossary_data = get_option('atfpp_glossary_data', []);
$glossary_instructions = '';
$matched_terms = [];
$has_glossary_terms = false;
if (!empty($glossary_data) && is_array($glossary_data)) {
// First, check if any strings contain glossary terms
foreach ($string_array as $string) {
foreach ($glossary_data as $entry) {
if (
is_array($entry) &&
!empty($entry['original_term']) &&
$entry['original_language_code'] === $source_language &&
stripos($string, $entry['original_term']) !== false
) {
$has_glossary_terms = true;
break 2; // Break both loops if we find a match
}
}
}
// Only process glossary terms if we found matches
if ($has_glossary_terms) {
// If strings parameter exists, decode it and check each string
$strings_to_check = [];
if (isset($params['strings'])) {
$decoded_strings = json_decode($params['strings'], true);
if (is_array($decoded_strings)) {
$strings_to_check = array_values($decoded_strings);
}
}
foreach ($glossary_data as $entry) {
// Skip invalid entries early
if (
!is_array($entry) ||
empty($entry['original_language_code']) ||
empty($entry['original_term']) ||
empty($entry['translations']) ||
$entry['original_language_code'] !== $source_language
) {
continue;
}
// Use array_column for faster lookup if translations is an array of arrays
$translations_by_code = [];
foreach ($entry['translations'] as $translation) {
if (
is_array($translation) &&
!empty($translation['target_language_code']) &&
!empty($translation['translated_term'])
) {
$translations_by_code[$translation['target_language_code']] = $translation['translated_term'];
}
}
// Check if the term exists in any of the strings
$term_found = false;
if (!empty($strings_to_check)) {
foreach ($strings_to_check as $string) {
if (stripos($string, $entry['original_term']) !== false) {
$term_found = true;
break;
}
}
} else {
// If no strings parameter, treat as found (original behavior)
$term_found = true;
}
if ($term_found && isset($translations_by_code[$target_language])) {
$matched_terms[] = [
'term' => $entry['original_term'],
'translation' => $translations_by_code[$target_language],
'description' => $entry['description'] ?? ''
];
}
}
}
}
// Build glossary instructions string only if we found matches
if ($has_glossary_terms && !empty($matched_terms)) {
$glossary_instructions = "Please use the following glossary terms in your translation:\n";
foreach ($matched_terms as $term) {
$src_term = isset($term['term']) ? (string) $term['term'] : '';
$translation = isset($term['translation']) ? (string) $term['translation'] : '';
$description = isset($term['description']) ? (string) $term['description'] : '';
$glossary_instructions .= "- \"{$src_term}\"\"{$translation}\"";
if ($description !== '') {
$glossary_instructions .= " — Note: {$description}";
}
$glossary_instructions .= "\n";
}
} else {
$glossary_instructions = "";
}
// Get the custom prompt
$custom_prompt = get_option('atfp_context_aware', '');
$ai_request_timeout = get_option('atfp_ai_request_timeout', 120);
// Only return the translation in the format of a JSON object with the keys being numeric values (matching the source keys), and the values being the translated strings
$content = sprintf(
'Instruction 1: Translate visible text content semantically into %s language. Provide a proper meaning-based translation.
Instruction 2: Do not translate or modify any content inside square brackets []. These are shortcodes or dynamic placeholders and must remain exactly as they are.
Instruction 3: Preserve all HTML tags and their attributes such as class, id, data-*, etc. Do not alter any part of the HTML structure.
Instruction 4: Return the translation in the format of a JSON object with the keys being numeric values (matching the source keys), and the values being the translated strings.
Instruction 5: Do not escape double quotes with backslashes. Output must be valid JSON without extra slashes.
Instruction 6: Translate the provided JSON array into %s language, regardless of whether the values are the same and Ensure the JSON is well-formed and complete.
Instruction 7: Decode any &lt; and &gt; HTML entities back to < and > symbols in the output & preserve and maintain whitespace.
Instruction 8: Return the output as a valid JSON object. Do not wrap the output in a string or markdown code block. Ensure the JSON is clean, parseable, and properly formatted. Please ensure that the output follows the format: {"key(numeric value)": "(translations of the strings in %s language)}" Strings are :- %s',
$target_language,
$target_language,
$target_language,
json_encode($strings)
);
// If the custom prompt is not empty, add it to the content
if ($custom_prompt && !empty($custom_prompt)) {
$content .= 'Instruction 9: ' . sanitize_text_field($custom_prompt);
}
if($service_slug === 'deepl'){
$data = array();
$strings=str_replace('\\n', '<ATFPP_NEW_L>', $strings);
$strings=str_replace('\\r', '<ATFPP_NEW_R>', $strings);
$data['text'] = json_decode($strings, true);
$data['target_lang'] = $target_language;
$data['tag_handling'] = 'html';
// Get DeepL API key from options
$deepl_api_key = get_option('Atfpp_Ai_Translate_deepl_api_key');
// var_dump($deepl_api_key);
// If we have glossary terms, create/use DeepL glossary
if ($has_glossary_terms && !empty($matched_terms)) {
// Format glossary entries for DeepL TSV format
$entries = '';
foreach ($matched_terms as $term) {
$entries .= $term['term'] . "\t" . $term['translation'] . "\n";
}
// Create glossary in DeepL
$glossary_response = wp_remote_post('https://api-free.deepl.com/v3/glossaries', [
'headers' => [
'Authorization' => 'DeepL-Auth-Key ' . $deepl_api_key,
'Content-Type' => 'application/json'
],
'body' => wp_json_encode([
'name' => 'AutoPoly Glossary ' . current_time('timestamp'),
'source_lang' => $source_language,
'target_lang' => $target_language,
'entries' => $entries,
'entries_format' => 'tsv'
]),
'timeout' => 20
]);
if (!is_wp_error($glossary_response)) {
$glossary_data = json_decode(wp_remote_retrieve_body($glossary_response), true);
if (isset($glossary_data['glossary_id'])) {
$data['glossary_id'] = $glossary_data['glossary_id'];
}
}
}
if(isset($custom_prompt) && !empty($custom_prompt)){
$data['context'] = $custom_prompt;
}
$content = json_encode($data);
}
// If glossary instructions are present, add them as a separate instruction
if ($glossary_instructions && !empty($glossary_instructions) && $service_slug !== 'deepl') {
$instruction_number = ($custom_prompt && !empty($custom_prompt)) ? 9 : 8;
$content .= 'Instruction ' . $instruction_number . ': ' . $glossary_instructions;
}
// Try to generate the text
try {
$model_params = array(
'feature' => 'my-test-feature',
'capabilities' => array(AI_Capability::TEXT_GENERATION),
);
if ( !empty($selected_model) ) {
$model_params['model'] = $selected_model;
}
$candidates = $service
->get_model($model_params, array('timeout' => (int) $ai_request_timeout))
->generate_text($content);
// Get the text from the candidates
$text = Helpers::get_text_from_contents(
Helpers::get_candidate_contents($candidates)
);
// Clean the text
$cleanText = preg_replace('/(^```json\n|```$)/', '', $text);
$cleanText=str_replace('<ATFPP_NEW_L>', '\n', $cleanText);
$cleanText=str_replace('<ATFPP_NEW_R>', '\r', $cleanText);
// Replace the double backslashes with a single backslash
$final_text = preg_replace('/\\\\{2,}([\'"n])/', '\\\$1', $cleanText);
$translated_text=json_decode($final_text, true);
if(is_array($translated_text) && count($translated_text) === 1){
$key=array_keys($translated_text)[0];
$orignal_string=json_decode($translated_text[$key], true);
if(isset($translated_text[$key]) && json_decode($translated_text[$key]) !== null && isset($orignal_string[$key]) && json_decode($orignal_string[$key]) === null){
$translated_text[$key]=json_decode($translated_text[$key], true)[$key];
$final_text=json_encode($translated_text);
}
}
// Return the data
$data = array('translate_data' => json_decode($final_text), 'text' => $cleanText);
// If we created a DeepL glossary, clean it up
if ($service_slug === 'deepl' && isset($glossary_data['glossary_id'])) {
wp_remote_request('https://api.deepl.com/v3/glossaries/' . $glossary_data['glossary_id'], [
'method' => 'DELETE',
'headers' => [
'Authorization' => 'DeepL-Auth-Key ' . $deepl_api_key
]
]);
}
// // Convert object to array for easier handling
// $translated_array = (array) $data['translate_data'];
// $string_array=json_decode($strings,true);
// global $wpdb;
// $table_name = $wpdb->prefix . 'atfpp_translations';
// // 1. Add the language column if it doesn't exist
// $column_exists = $wpdb->get_var(
// $wpdb->prepare("SHOW COLUMNS FROM `$table_name` LIKE %s", $target_language)
// );
// if (!$column_exists) {
// $wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `$target_language` VARCHAR(255)");
// }
// // 2. Add the source language column if it doesn't exist (for original strings)
// $source_column_exists = $wpdb->get_var(
// $wpdb->prepare("SHOW COLUMNS FROM `$table_name` LIKE %s", $source_language)
// );
// if (!$source_column_exists) {
// $wpdb->query("ALTER TABLE `$table_name` ADD COLUMN `$source_language` VARCHAR(255)");
// }
// // 3. Insert or update each translation
// foreach ($string_array as $index => $original) {
// $translation = isset($translated_array[$index]) ? $translated_array[$index] : '';
// // Check if a row exists for this id (index+1)
// $row = $wpdb->get_row(
// $wpdb->prepare("SELECT id FROM `$table_name` WHERE `$source_language` = %s", $original)
// );
// if ($row) {
// // Update the translation in the language column
// $wpdb->update(
// $table_name,
// array(
// $target_language => $translation
// ),
// array($source_language => $original)
// );
// } else {
// // Insert a new row with the original string and the translation
// $wpdb->insert(
// $table_name,
// array(
// $source_language => $original,
// $target_language => $translation
// )
// );
// }
// }
wp_send_json_success($data);
} catch (Exception $e) {
wp_send_json_error('Error during text generation: ' . $e->getMessage());
}
} else {
wp_send_json_error(sprintf(
'%s service is not available.',
$service_slug === 'google' ? 'GeminiAI' : ucfirst($service_slug)
));
}
}
wp_send_json_error('You are not authorized to perform this action.');
}
public function bulk_translate_entries($params)
{
// Check if the user is logged in and has the necessary capabilities
if (!is_user_logged_in()) wp_send_json_error('You are not authorized to perform this action.');
if (!current_user_can('edit_posts')) wp_send_json_error('You are not authorized to perform this action.');
// Verify the nonce
if (!wp_verify_nonce($params['privateKey'], 'atfpp_bulk_translate_entries_nonce')) wp_send_json_error('You are not authorized to perform this action.');
global $polylang;
$slug_translation_option = get_option('atfp_slug_translation_option','title_translate');
// check language exists or not
$translate_lang = json_decode($params['lang']);
$post_ids=json_decode($params['ids']);
$posts_translate=array();
$gutenberg_block=false;
$post_meta_sync = true;
if (!isset(PLL()->options['sync']) || (isset(PLL()->options['sync']) && !in_array('post_meta', PLL()->options['sync']))) {
$post_meta_sync = false;
}
if(count($translate_lang) > 0 && !(count($post_ids) < 1)){
$pll_langs=$polylang->model->get_languages_list();
$pll_langs_slugs=array_column($pll_langs, 'slug');
$allowed_meta_fields=ATFPP_Helper::get_instance()->get_allowed_custom_fields();
foreach($post_ids as $postId){
if(!current_user_can('edit_post', $postId)) continue;
$posts_translate[$postId]['sourceLanguage']=$polylang->model->post->get_language($postId)->slug;
$post_data=get_post($postId);
if(!$posts_translate[$postId]['sourceLanguage']){
$posts_translate[$postId]['sourceLanguage']=false;
$posts_translate[$postId]['title']=$post_data->post_title;
$posts_translate[$postId]['editor_type']=has_blocks($post_data->post_content) ? 'block' : 'classic';
$posts_translate[$postId]['post_link']=html_entity_decode( get_edit_post_link( $postId ) );
continue;
}
$elementor_enabled=get_post_meta($postId,'_elementor_edit_mode', true);
if(!$post_data) continue;
if($slug_translation_option === 'slug_translate'){
$posts_translate[$postId]['post_name']=urldecode(get_post_field('post_name', $postId));
}
$posts_translate[$postId]['title']=$post_data->post_title;
$posts_translate[$postId]['content']=has_blocks($post_data->post_content) ? parse_blocks($post_data->post_content) : $post_data->post_content;
$posts_translate[$postId]['content']=has_blocks($post_data->post_content) ? parse_blocks($post_data->post_content) : $post_data->post_content;
$posts_translate[$postId]['editor_type']=has_blocks($post_data->post_content) ? 'block' : 'classic';
if(isset($post_data->post_excerpt) && !empty($post_data->post_excerpt)){
$posts_translate[$postId]['excerpt']=$post_data->post_excerpt;
}
$posts_translate[$postId]['sourceLanguage']=!isset($posts_translate[$postId]['sourceLanguage']) ? pll_default_language() : $posts_translate[$postId]['sourceLanguage'];
if(!$post_meta_sync){
$post_meta_fields=get_post_meta($postId);
$existed_meta_fields=array_intersect(array_keys($post_meta_fields), array_keys($allowed_meta_fields));
foreach($existed_meta_fields as $key){
if(isset($post_meta_fields[$key]) && !empty($post_meta_fields[$key]) && isset($allowed_meta_fields[$key]['status']) && true === $allowed_meta_fields[$key]['status']){
$value=$allowed_meta_fields[$key]['type'] && is_array($post_meta_fields[$key]) ? maybe_unserialize($post_meta_fields[$key][0]) : maybe_unserialize($post_meta_fields[$key]);
$posts_translate[$postId]['metaFields'][$key]=$value;
}
}
}
$posts_translate[$postId]['post_link']=get_the_permalink($postId);
if($elementor_enabled && 'builder' === $elementor_enabled && defined('ELEMENTOR_VERSION')){
$elementor_data=get_post_meta($postId,'_elementor_data', true);
if($elementor_data && '' !== $elementor_data){
$posts_translate[$postId]['editor_type']='elementor';
$elementor_data = array();
if(class_exists('\Elementor\Plugin') && property_exists('\Elementor\Plugin', 'instance') ){
$elementor_data=\Elementor\Plugin::$instance->documents->get($postId)->get_elements_data();
}
$posts_translate[$postId]['content']=$elementor_data;
unset($posts_translate[$postId]['metaFields']['_elementor_data']);
}
}
if($posts_translate[$postId]['editor_type'] === 'block' && !$gutenberg_block){
$gutenberg_block=true;
}
foreach($translate_lang as $lang){
if(in_array($lang, $pll_langs_slugs)){
$post_translate_status = $polylang->model->post->get_translation( $postId, $lang );
if(!$post_translate_status){
$posts_translate[$postId]['languages'][]=$lang;
}else{
$posts_translate[$postId]['postExists'][$lang]=array(
'post_title'=>get_the_title($post_translate_status),
'post_url'=>get_the_permalink($post_translate_status)
);
}
}
}
}
}
$data=array('posts'=>$posts_translate, 'CreateTranslatePostNonce'=>wp_create_nonce('atfpp_create_translate_post_nonce'));
if(!$post_meta_sync){
$data['allowedMetaFields']=json_encode($allowed_meta_fields);
}
if($gutenberg_block){
$block_parse_rules = ATFPP_Helper::get_instance()->get_block_parse_rules();
$data['blockParseRules']=json_encode($block_parse_rules);
}
if(count($posts_translate) > 0){
wp_send_json_success($data);
}else{
wp_send_json_error('No posts to translate');
};
}
public function create_translate_post($params){
if(!isset($params['source_language']) || empty($params['source_language'])) wp_send_json_error('Invalid source language');
if(!isset($params['post_id']) || !isset($params['target_language']) || (!isset($params['post_title']) && !isset($params['post_content']))) wp_send_json_error('Invalid request');
if(!isset($params['target_language']) && empty($params['target_language'])) wp_send_json_error('Invalid target language');
if(!wp_verify_nonce($params['privateKey'], 'atfpp_create_translate_post_nonce')) wp_send_json_error('You are not authorized to perform this action.');
if(empty($params['post_title']) && empty($params['post_content'])) wp_send_json_error('Invalid request content & title empty');
$params = $params->get_params();
$post_id=intval(sanitize_text_field($params['post_id']));
$target_language=sanitize_text_field($params['target_language']);
$editor_type=sanitize_text_field($params['editor_type']);
$source_language=sanitize_text_field($params['source_language']);
$title=isset($params['post_title']) ? sanitize_text_field($params['post_title']) : '';
$slug=isset($params['post_name']) && !empty($params['post_name']) ? sanitize_text_field($params['post_name']) : false;
$excerpt=isset($params['post_excerpt']) ? sanitize_text_field($params['post_excerpt']) : '';
$content=isset($params['post_content']) ? $params['post_content'] : '';
$meta_fields=isset($params['post_meta_fields']) ? $params['post_meta_fields'] : '';
if(!current_user_can('edit_post', $post_id)) wp_send_json_error('You are not authorized to perform this action.');
$post_data=array(
'post_title'=>sanitize_text_field($title),
'post_content'=>$content,
);
if($excerpt && !empty($excerpt)){
$post_data['post_excerpt']=sanitize_text_field($excerpt);
}
if($meta_fields && !empty($meta_fields)){
$post_data['post_meta_fields']=json_decode($meta_fields, true);
}
$slug_translation_option = get_option('atfp_slug_translation_option','title_translate');
if($slug_translation_option === 'slug_translate' && $slug && !empty($slug)){
$post_data['post_name']=sanitize_title($slug);
}else if($slug_translation_option === 'slug_keep'){
$post_data['post_name']=sanitize_text_field(get_post_field('post_name', $post_id));
}else{
$post_data['post_name']=sanitize_title($title);
};
if($editor_type === 'elementor'){
$post_data['meta_fields']['_elementor_data']=$content;
unset($post_data['post_content']);
}else if($editor_type === 'block'){
$post_data['post_content']=serialize_blocks(json_decode($post_data['post_content'], true));
}else if($editor_type === 'classic'){
$post_data['post_content']=wp_kses_post(json_decode($params['post_content'], true));
}else{
$post_data['post_content']=wp_kses_post($post_data['post_content']);
}
global $polylang;
$post_clone = new ATFP_Posts_Clone($polylang);
$post_id = $post_clone->copy_post( $post_id, $source_language, $target_language, false, $post_data, $editor_type );
if(!$post_id){
wp_send_json_error('Unable to create the translated post for parent post ID ' . $post_id . ' in ' . $target_language . '.');
}else{
$post_link=html_entity_decode(get_the_permalink($post_id));
$post_title=html_entity_decode(get_the_title($post_id));
$post_edit_link=html_entity_decode(get_edit_post_link($post_id));
wp_send_json_success(array('post_id'=>$post_id, 'target_language'=>$target_language, 'post_link'=>$post_link, 'post_title'=>$post_title, 'post_edit_link'=>$post_edit_link, 'update_translate_data_nonce'=>wp_create_nonce('atfp_translate_nonce')));
}
}
public function bulk_translate_taxonomy_entries($params){
if(!isset($params['taxonomy']) || empty($params['taxonomy'])) wp_send_json_error('Invalid taxonomy');
if(!isset($params['lang']) || empty($params['lang'])) wp_send_json_error('Invalid language');
if(!isset($params['privateKey']) || empty($params['privateKey'])) wp_send_json_error('Invalid private key');
if(!isset($params['ids']) || empty($params['ids'])) wp_send_json_error('Invalid ids');
// Check if the user is logged in and has the necessary capabilities
if (!is_user_logged_in()) wp_send_json_error('You are not authorized to perform this action.');
if (!current_user_can('edit_posts')) wp_send_json_error('You are not authorized to perform this action.');
$params = $params->get_params();
$slug_translation_option = get_option('atfp_slug_translation_option','title_translate');
// Verify the nonce
if (!wp_verify_nonce($params['privateKey'], 'atfpp_bulk_translate_entries_nonce')) wp_send_json_error('You are not authorized to perform this action.');
$translate_lang=json_decode($params['lang']);
$taxonomy_translate=array();
if($translate_lang && count($translate_lang) > 0){
global $polylang;
$pll_langs=$polylang->model->get_languages_list();
$pll_langs_slugs=array_column($pll_langs, 'slug');
$taxonomy=sanitize_text_field($params['taxonomy']);
$taxonomy_ids=json_decode($params['ids']);
foreach($taxonomy_ids as $taxonomy_id){
$taxonomy_translate[$taxonomy_id]['sourceLanguage']=pll_get_term_language($taxonomy_id);
$taxonomy_data=get_term($taxonomy_id, $taxonomy);
if(!$taxonomy_translate[$taxonomy_id]['sourceLanguage']){
$taxonomy_translate[$taxonomy_id]['sourceLanguage']=false;
$taxonomy_translate[$taxonomy_id]['title']=$taxonomy_data->name;
$taxonomy_translate[$taxonomy_id]['editor_type']='taxonomy';
$taxonomy_translate[$taxonomy_id]['post_link']=html_entity_decode(get_edit_term_link($taxonomy_data->term_id, $taxonomy_data->taxonomy));
continue;
}
$taxonomy_translate[$taxonomy_id]['title']=$taxonomy_data->name;
if($slug_translation_option === 'slug_translate'){
$taxonomy_translate[$taxonomy_id]['post_name']=urldecode($taxonomy_data->slug);
}
$taxonomy_translate[$taxonomy_id]['editor_type']='taxonomy';
if($taxonomy_data->description && !empty($taxonomy_data->description)){
$taxonomy_translate[$taxonomy_id]['content']=$taxonomy_data->description;
}
foreach($translate_lang as $lang){
if(in_array($lang, $pll_langs_slugs)){
$post_translate_status = pll_get_term( $taxonomy_id, $lang );
if(!$post_translate_status){
$taxonomy_translate[$taxonomy_id]['languages'][]=$lang;
}else{
$term = get_term( $post_translate_status, $taxonomy );
$title=isset($term->name) ? $term->name : '';
$slug=get_term_link($post_translate_status, $taxonomy);
if(is_wp_error($slug) || empty($slug)){
$slug='';
}
$taxonomy_translate[$taxonomy_id]['postExists'][$lang]=array(
'post_title'=>$title,
'post_url'=>$slug,
);
}
}
}
}
}
$data=array('posts'=>$taxonomy_translate, 'CreateTranslatePostNonce'=>wp_create_nonce('atfpp_create_translate_taxonomy_nonce'));
if(count($taxonomy_translate) > 0){
wp_send_json_success($data);
}else{
wp_send_json_error('No taxonomy posts to translate');
};
}
public function create_translate_taxonomy($params){
if(!isset($params['term_id']) || empty($params['term_id'])) wp_send_json_error('Invalid term id');
if(!isset($params['target_language']) || empty($params['target_language'])) wp_send_json_error('Invalid target language');
if(!isset($params['taxonomy']) || empty($params['taxonomy'])) wp_send_json_error('Invalid taxonomy');
if(!isset($params['source_language']) || empty($params['source_language'])) wp_send_json_error('Invalid source language');
if(!wp_verify_nonce($params['privateKey'], 'atfpp_create_translate_taxonomy_nonce')) wp_send_json_error('You are not authorized to perform this action.');
$params = $params->get_params();
$term_id=intval(sanitize_text_field($params['term_id']));
$target_language=isset($params['target_language']) ? sanitize_text_field($params['target_language']) : '';
$taxonomy=isset($params['taxonomy']) ? sanitize_text_field($params['taxonomy']) : '';
$taxonomy_name=isset($params['taxonomy_name']) ? sanitize_text_field($params['taxonomy_name']) : '';
$taxonomy_slug=isset($params['taxonomy_slug']) ? sanitize_title($params['taxonomy_slug']) : '';
$taxonomy_description=isset($params['taxonomy_description']) ? wp_kses_post($params['taxonomy_description']) : '';
$slug_translation_option = get_option('atfp_slug_translation_option','title_translate');
if(!$target_language) wp_send_json_error('Invalid target language');
if(!$taxonomy) wp_send_json_error('Invalid taxonomy');
$get_term=get_term($term_id, $taxonomy);
$translations=new Translations();
if($taxonomy_name && !empty($taxonomy_name)){
$entry = $this->create_translation_entry($get_term->name, $taxonomy_name, 'name');
$translations->add_entry($entry);
}
if($taxonomy_description && !empty($taxonomy_description)){
$entry = $this->create_translation_entry($get_term->description, $taxonomy_description, 'description');
$translations->add_entry($entry);
}
if($slug_translation_option === 'slug_translate' && $taxonomy_slug && !empty($taxonomy_slug)){
$taxonomy_slug=sanitize_title($taxonomy_slug);
}else if($slug_translation_option === 'slug_keep'){
$taxonomy_slug=sanitize_text_field($get_term->slug);
}else{
$taxonomy_slug=sanitize_title($taxonomy_name);
};
if($taxonomy_slug && !empty($taxonomy_slug)){
$entry = $this->create_translation_entry($get_term->slug, $taxonomy_slug, 'slug');
$translations->add_entry($entry);
}
global $polylang;
$target_language_object = $polylang->model->get_language($target_language);
$post_clone = new ATFP_Term_Clone($polylang);
$term_id = $post_clone->translate( array('id'=>$term_id, 'data'=>$translations), $target_language_object );
if(!$term_id){
wp_send_json_error('Unable to create the translated post for parent post ID ' . $term_id . ' in ' . $target_language_object . '.');
exit;
}
$term_url=get_term_link($term_id, $taxonomy);
$term = get_term( $term_id, $taxonomy );
$term_title=html_entity_decode($term->name);
$term_link=$term_url && is_string($term_url) ? html_entity_decode($term_url) : '';
$term_edit_link=html_entity_decode(get_edit_term_link($term_id, $taxonomy));
wp_send_json_success(array('post_id'=>$term_id, 'target_language'=>$target_language, 'post_link'=>$term_link, 'post_title'=>$term_title, 'post_edit_link'=>$term_edit_link, 'update_translate_data_nonce'=>wp_create_nonce('atfp_translate_nonce')));
}
public function create_translation_entry($singular, $translation, $context){
$entry = new Translation_Entry(
array(
'singular'=>$singular,
'translation'=>array($translation),
'context'=>$context,
)
);
return $entry;
}
}
endif;