| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | namespace WPM\Includes; |
|---|
| 4 | use WPM\Includes\Admin\WPM_Reset_Settings; |
|---|
| 5 | use WPM\Includes\Admin\WPM_OpenAI; |
|---|
| 6 | use WPM\Includes\Admin\Settings\WPM_Settings_Auto_Translate_Pro; |
|---|
| 7 | use WPM\Includes\Admin\Settings\WPM_Settings_AI_Integration; |
|---|
| 8 | |
|---|
| 9 | if ( ! defined( 'ABSPATH' ) ) { |
|---|
| 10 | exit; // Exit if accessed directly |
|---|
| 11 | } |
|---|
| 12 | |
|---|
| 13 | /** |
|---|
| 14 | * WP_Multilang WPM_AJAX. |
|---|
| 15 | * |
|---|
| 16 | * AJAX Event Handler. |
|---|
| 17 | * |
|---|
| 18 | * @class WPM_AJAX |
|---|
| 19 | * @package WPM/Classes |
|---|
| 20 | * @category Class |
|---|
| 21 | * @author Valentyn Riaboshtan |
|---|
| 22 | */ |
|---|
| 23 | class WPM_AJAX { |
|---|
| 24 | |
|---|
| 25 | /** |
|---|
| 26 | * Hook in ajax handlers. |
|---|
| 27 | */ |
|---|
| 28 | public static function init() { |
|---|
| 29 | add_action( 'init', array( __CLASS__, 'define_ajax' ), 0 ); |
|---|
| 30 | add_action( 'template_redirect', array( __CLASS__, 'do_wpm_ajax' ), 0 ); |
|---|
| 31 | self::add_ajax_events(); |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | /** |
|---|
| 35 | * Get WPM Ajax Endpoint. |
|---|
| 36 | * |
|---|
| 37 | * @param string $request Optional |
|---|
| 38 | * |
|---|
| 39 | * @return string |
|---|
| 40 | */ |
|---|
| 41 | public static function get_endpoint( $request = '' ) { |
|---|
| 42 | return esc_url_raw( add_query_arg( 'wpm-ajax', $request ) ); |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | /** |
|---|
| 46 | * Set WPM AJAX constant and headers. |
|---|
| 47 | */ |
|---|
| 48 | public static function define_ajax() { |
|---|
| 49 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- this is a dependent function and its all security measurament is done wherever it has been used. |
|---|
| 50 | if ( ! empty( $_GET['wpm-ajax'] ) ) { |
|---|
| 51 | if ( ! wp_doing_ajax() ) { |
|---|
| 52 | define( 'DOING_AJAX', true ); |
|---|
| 53 | } |
|---|
| 54 | if ( ! defined( 'WPM_DOING_AJAX' ) ) { |
|---|
| 55 | define( 'WPM_DOING_AJAX', true ); |
|---|
| 56 | } |
|---|
| 57 | // Turn off display_errors during AJAX events to prevent malformed JSON |
|---|
| 58 | if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) { |
|---|
| 59 | // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged --Reason Turn off display_errors during AJAX events to prevent malformed JSON |
|---|
| 60 | @ini_set( 'display_errors', 0 ); |
|---|
| 61 | } |
|---|
| 62 | $GLOBALS['wpdb']->hide_errors(); |
|---|
| 63 | } |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | /** |
|---|
| 67 | * Send headers for WPM Ajax Requests |
|---|
| 68 | */ |
|---|
| 69 | private static function wpm_ajax_headers() { |
|---|
| 70 | send_origin_headers(); |
|---|
| 71 | @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); |
|---|
| 72 | @header( 'X-Robots-Tag: noindex' ); |
|---|
| 73 | send_nosniff_header(); |
|---|
| 74 | nocache_headers(); |
|---|
| 75 | status_header( 200 ); |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | /** |
|---|
| 79 | * Check for WPM Ajax request and fire action. |
|---|
| 80 | */ |
|---|
| 81 | public static function do_wpm_ajax() { |
|---|
| 82 | global $wp_query; |
|---|
| 83 | |
|---|
| 84 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- this is a dependent function and its all security measurament is done wherever it has been used. |
|---|
| 85 | if ( ! empty( $_GET['wpm-ajax'] ) ) { |
|---|
| 86 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- this is a dependent function and its all security measurament is done wherever it has been used. |
|---|
| 87 | $wp_query->set( 'wpm-ajax', sanitize_text_field( $_GET['wpm-ajax'] ) ); |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | if ( $action = $wp_query->get( 'wpm-ajax' ) ) { |
|---|
| 91 | self::wpm_ajax_headers(); |
|---|
| 92 | do_action( 'wpm_ajax_' . sanitize_text_field( $action ) ); |
|---|
| 93 | die(); |
|---|
| 94 | } |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | /** |
|---|
| 98 | * Hook in methods - uses WordPress ajax handlers (admin-ajax). |
|---|
| 99 | */ |
|---|
| 100 | public static function add_ajax_events() { |
|---|
| 101 | $ajax_events = array( |
|---|
| 102 | 'delete_lang' => false, |
|---|
| 103 | 'delete_localization' => false, |
|---|
| 104 | 'qtx_import' => false, |
|---|
| 105 | 'rated' => false, |
|---|
| 106 | 'send_query_message' => false, |
|---|
| 107 | 'deactivate_plugin' => false, |
|---|
| 108 | 'subscribe_to_news_letter' => false, |
|---|
| 109 | 'newsletter_hide_form' => false, |
|---|
| 110 | 'settings_newsletter_submit' => false, |
|---|
| 111 | 'block_lang_switcher' => true, |
|---|
| 112 | 'reset_settings' => true, |
|---|
| 113 | 'validate_secret_key' => false, |
|---|
| 114 | 'save_openai_settings' => false, |
|---|
| 115 | 'do_auto_translate' => false, |
|---|
| 116 | 'singlular_auto_translate' => false, |
|---|
| 117 | 'singlular_auto_translate_term' => false, |
|---|
| 118 | 'get_translation_node_count' => false, |
|---|
| 119 | 'process_batch_translation' => false, |
|---|
| 120 | 'check_ai_platform_quota' => false, |
|---|
| 121 | ); |
|---|
| 122 | |
|---|
| 123 | foreach ( $ajax_events as $ajax_event => $nopriv ) { |
|---|
| 124 | add_action( 'wp_ajax_wpm_' . $ajax_event, array( __CLASS__, $ajax_event ) ); |
|---|
| 125 | |
|---|
| 126 | if ( $nopriv ) { |
|---|
| 127 | add_action( 'wp_ajax_nopriv_wpm_' . $ajax_event, array( __CLASS__, $ajax_event ) ); |
|---|
| 128 | |
|---|
| 129 | // GP AJAX can be used for frontend ajax requests |
|---|
| 130 | add_action( 'wpm_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) ); |
|---|
| 131 | } |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | // Add direct AJAX action for wpmpro_search_items |
|---|
| 135 | add_action( 'wp_ajax_wpmpro_search_items', array( __CLASS__, 'wpmpro_search_items' ) ); |
|---|
| 136 | |
|---|
| 137 | // Add direct AJAX action for bulk auto translate |
|---|
| 138 | add_action( 'wp_ajax_wpm_do_auto_translate', array( __CLASS__, 'do_auto_translate' ) ); |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | /** |
|---|
| 142 | * Remove installed language files and option |
|---|
| 143 | */ |
|---|
| 144 | public static function delete_lang() { |
|---|
| 145 | |
|---|
| 146 | check_ajax_referer( 'delete-lang', 'security' ); |
|---|
| 147 | |
|---|
| 148 | $language = wpm_get_post_data_by_key( 'language' ); |
|---|
| 149 | $options = wpm_get_lang_option(); |
|---|
| 150 | |
|---|
| 151 | if ( ! $language || ! isset( $options[ $language ] ) || ( wpm_get_user_language() === $language ) || ( wpm_get_default_language() === $language ) ) { |
|---|
| 152 | return; |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | unset( $options[ $language ] ); |
|---|
| 156 | |
|---|
| 157 | global $wpdb; |
|---|
| 158 | //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: using WP bulit in function updates the option of current language which does not work for our plugin in this case |
|---|
| 159 | $wpdb->update( $wpdb->options, array( 'option_value' => maybe_serialize( $options ) ), array( 'option_name' => 'wpm_languages' ) ); |
|---|
| 160 | |
|---|
| 161 | die(); |
|---|
| 162 | } |
|---|
| 163 | |
|---|
| 164 | /** |
|---|
| 165 | * Remove installed language files and option |
|---|
| 166 | */ |
|---|
| 167 | public static function delete_localization() { |
|---|
| 168 | |
|---|
| 169 | check_ajax_referer( 'delete-localization', 'security' ); |
|---|
| 170 | |
|---|
| 171 | $locale = wpm_get_post_data_by_key( 'locale' ); |
|---|
| 172 | $options = wpm_get_lang_option(); |
|---|
| 173 | |
|---|
| 174 | if ( ! $locale ) { |
|---|
| 175 | wp_send_json_error( __( 'No locale sending', 'wp-multilang' ) ); |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | foreach ( $options as $language ) { |
|---|
| 179 | if ( $language['translation'] == $locale ) { |
|---|
| 180 | wp_send_json_error( __( 'Localization using', 'wp-multilang' ) ); |
|---|
| 181 | } |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | $files_delete = array(); |
|---|
| 185 | $installed_plugin_translations = wp_get_installed_translations( 'plugins' ); |
|---|
| 186 | |
|---|
| 187 | foreach ( $installed_plugin_translations as $plugin => $translation ) { |
|---|
| 188 | if ( isset( $translation[ $locale ] ) ) { |
|---|
| 189 | $files_delete[] = WP_LANG_DIR . '/plugins/' . $plugin . '-' . $locale . '.mo'; |
|---|
| 190 | $files_delete[] = WP_LANG_DIR . '/plugins/' . $plugin . '-' . $locale . '.po'; |
|---|
| 191 | } |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | $installed_themes_translations = wp_get_installed_translations( 'themes' ); |
|---|
| 195 | |
|---|
| 196 | foreach ( $installed_themes_translations as $theme => $translation ) { |
|---|
| 197 | if ( isset( $translation[ $locale ] ) ) { |
|---|
| 198 | $files_delete[] = WP_LANG_DIR . '/themes/' . $theme . '-' . $locale . '.mo'; |
|---|
| 199 | $files_delete[] = WP_LANG_DIR . '/themes/' . $theme . '-' . $locale . '.po'; |
|---|
| 200 | } |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | $installed_core_translations = wp_get_installed_translations( 'core' ); |
|---|
| 204 | |
|---|
| 205 | foreach ( $installed_core_translations as $wp_file => $translation ) { |
|---|
| 206 | if ( isset( $translation[ $locale ] ) ) { |
|---|
| 207 | $files_delete[] = WP_LANG_DIR . '/' . $wp_file . '-' . $locale . '.mo'; |
|---|
| 208 | $files_delete[] = WP_LANG_DIR . '/' . $wp_file . '-' . $locale . '.po'; |
|---|
| 209 | } |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | $files_delete[] = WP_LANG_DIR . '/' . $locale . '.po'; |
|---|
| 213 | $files_delete[] = WP_LANG_DIR . '/' . $locale . '.mo'; |
|---|
| 214 | |
|---|
| 215 | foreach ( $files_delete as $file ) { |
|---|
| 216 | wp_delete_file( $file ); |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | wp_send_json_success( esc_html__( 'Localization deleted', 'wp-multilang' ) ); |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | /** |
|---|
| 223 | * Import translations for terms from qTranslate |
|---|
| 224 | * |
|---|
| 225 | * @author Soft79 |
|---|
| 226 | */ |
|---|
| 227 | public static function qtx_import() { |
|---|
| 228 | |
|---|
| 229 | check_ajax_referer( 'qtx-import', 'security' ); |
|---|
| 230 | |
|---|
| 231 | $term_count = 0; |
|---|
| 232 | |
|---|
| 233 | if ( $qtranslate_terms = get_option( 'qtranslate_term_name', array() ) ) { |
|---|
| 234 | |
|---|
| 235 | $taxonomies = get_taxonomies(); |
|---|
| 236 | $terms = get_terms( array('taxonomy' => $taxonomies, 'hide_empty' => false ) ); |
|---|
| 237 | |
|---|
| 238 | foreach ( $terms as $term ) { |
|---|
| 239 | $original = $term->name; |
|---|
| 240 | |
|---|
| 241 | //Translation available? |
|---|
| 242 | if ( ! isset( $qtranslate_terms[ $original ] ) ) { |
|---|
| 243 | continue; |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | //Translate the name |
|---|
| 247 | $strings = wpm_value_to_ml_array( $original ); |
|---|
| 248 | foreach ( $qtranslate_terms[ $original ] as $code => $translation ) { |
|---|
| 249 | $strings = wpm_set_language_value( $strings, $translation, array(), $code ); |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | //Update the name |
|---|
| 253 | $term->name = wpm_ml_value_to_string( $strings ); |
|---|
| 254 | if ( $term->name !== $original ) { |
|---|
| 255 | $result = wp_update_term( $term->term_id, $term->taxonomy, array( 'name' => $term->name ) ); |
|---|
| 256 | if ( ! is_wp_error( $result ) ) { |
|---|
| 257 | $term_count++; |
|---|
| 258 | } |
|---|
| 259 | } |
|---|
| 260 | } |
|---|
| 261 | |
|---|
| 262 | delete_option( 'qtranslate_term_name' ); |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | /* translators: %d: This will get the number of term counts. */ |
|---|
| 266 | wp_send_json( sprintf( __( '%d terms were imported successfully.', 'wp-multilang' ), $term_count ) ); |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | /** |
|---|
| 270 | * Triggered when clicking the rating footer. |
|---|
| 271 | */ |
|---|
| 272 | public static function rated() { |
|---|
| 273 | if ( ! current_user_can( 'manage_translations' ) ) { |
|---|
| 274 | wp_die( -1 ); |
|---|
| 275 | } |
|---|
| 276 | update_option( 'wpm_admin_footer_text_rated', 1 ); |
|---|
| 277 | wp_die(); |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | /** |
|---|
| 281 | * Triggered when any support query is sent from Help & Support tab |
|---|
| 282 | * @since 2.4.2 |
|---|
| 283 | * */ |
|---|
| 284 | public static function send_query_message() |
|---|
| 285 | { |
|---|
| 286 | check_ajax_referer( 'support-localization', 'security' ); |
|---|
| 287 | |
|---|
| 288 | if ( ! current_user_can( 'manage_translations' ) ) { |
|---|
| 289 | wp_die( -1 ); |
|---|
| 290 | } |
|---|
| 291 | |
|---|
| 292 | if ( isset( $_POST['message'] ) && isset( $_POST['email'] ) ) { |
|---|
| 293 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 294 | $message = sanitize_textarea_field( $_POST['message'] ); |
|---|
| 295 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 296 | $email = sanitize_email( $_POST['email'] ); |
|---|
| 297 | |
|---|
| 298 | if(function_exists('wp_get_current_user')){ |
|---|
| 299 | |
|---|
| 300 | $user = wp_get_current_user(); |
|---|
| 301 | |
|---|
| 302 | $message = '<p>'.esc_html($message).'</p><br><br>'.'Query from WP Multilang plugin support tab'; |
|---|
| 303 | |
|---|
| 304 | $user_data = $user->data; |
|---|
| 305 | $user_email = $user_data->user_email; |
|---|
| 306 | |
|---|
| 307 | if($email){ |
|---|
| 308 | $user_email = $email; |
|---|
| 309 | } |
|---|
| 310 | //php mailer variables |
|---|
| 311 | $sendto = 'team@magazine3.in'; |
|---|
| 312 | $subject = "WP Multilang Query"; |
|---|
| 313 | |
|---|
| 314 | $headers[] = 'Content-Type: text/html; charset=UTF-8'; |
|---|
| 315 | $headers[] = 'From: '. esc_attr($user_email); |
|---|
| 316 | $headers[] = 'Reply-To: ' . esc_attr($user_email); |
|---|
| 317 | // Load WP components, no themes. |
|---|
| 318 | |
|---|
| 319 | $sent = wp_mail($sendto, $subject, $message, $headers); |
|---|
| 320 | |
|---|
| 321 | if($sent){ |
|---|
| 322 | |
|---|
| 323 | echo wp_json_encode(array('status'=>'t')); |
|---|
| 324 | |
|---|
| 325 | }else{ |
|---|
| 326 | |
|---|
| 327 | echo wp_json_encode(array('status'=>'f')); |
|---|
| 328 | |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | } |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | wp_die(); |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | /** |
|---|
| 338 | * Trigger when delete translation check box checked from popup modal when deactivating the plugin |
|---|
| 339 | * @since 2.4.17 |
|---|
| 340 | * */ |
|---|
| 341 | public static function deactivate_plugin() |
|---|
| 342 | { |
|---|
| 343 | // if ( ! current_user_can( 'manage_translations' ) ) { |
|---|
| 344 | // wp_die( -1 ); |
|---|
| 345 | // } |
|---|
| 346 | |
|---|
| 347 | // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Security measurament is done below in this function with nonce key wpm_feedback_nonce. |
|---|
| 348 | if( isset( $_POST['data'] ) ) { |
|---|
| 349 | // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: Sanitization is handled below in this function |
|---|
| 350 | parse_str( $_POST['data'], $data ); |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | if ( ! isset( $data['wpm_deactivate_plugin_nonce'] ) ) { |
|---|
| 354 | wp_die( -1 ); |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | if ( ! wp_verify_nonce( $data['wpm_deactivate_plugin_nonce'], 'wpm_deactivate_plugin_nonce' ) ) { |
|---|
| 358 | wp_die( -1 ); |
|---|
| 359 | } |
|---|
| 360 | |
|---|
| 361 | // Reset translation data |
|---|
| 362 | if ( ! empty( $data['wpm_uninstall_translations'] ) ) { |
|---|
| 363 | |
|---|
| 364 | if ( class_exists('WPM\Includes\Admin\WPM_Reset_Settings') ) { |
|---|
| 365 | |
|---|
| 366 | \WPM\Includes\Admin\WPM_Reset_Settings::wpm_uninstall_translations_data(); |
|---|
| 367 | } |
|---|
| 368 | |
|---|
| 369 | } |
|---|
| 370 | |
|---|
| 371 | wp_die(); |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | /** |
|---|
| 375 | * Triggered when any newsletter subscribe button is clicked |
|---|
| 376 | * @since 2.4.7 |
|---|
| 377 | * */ |
|---|
| 378 | public static function subscribe_to_news_letter(){ |
|---|
| 379 | |
|---|
| 380 | if(!current_user_can('manage_options')){ |
|---|
| 381 | wp_die( -1 ); |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | if ( ! isset( $_POST['wpm_security_nonce'] ) ){ |
|---|
| 385 | wp_die( -1 ); |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 389 | if ( !wp_verify_nonce( $_POST['wpm_security_nonce'], 'wpm_security_nonce' ) ){ |
|---|
| 390 | wp_die( -1 ); |
|---|
| 391 | } |
|---|
| 392 | |
|---|
| 393 | $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; |
|---|
| 394 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash |
|---|
| 395 | $email = isset( $_POST['email'] ) ? sanitize_email( $_POST['email'] ) : ''; |
|---|
| 396 | $website = isset( $_POST['website'] ) ? sanitize_text_field( wp_unslash( $_POST['website'] ) ) : ''; |
|---|
| 397 | |
|---|
| 398 | if($email){ |
|---|
| 399 | |
|---|
| 400 | $api_url = 'http://magazine3.company/wp-json/api/central/email/subscribe'; |
|---|
| 401 | |
|---|
| 402 | $api_params = array( |
|---|
| 403 | 'name' => $name, |
|---|
| 404 | 'email' => $email, |
|---|
| 405 | 'website' => $website, |
|---|
| 406 | 'type' => 'wpmultilang', |
|---|
| 407 | ); |
|---|
| 408 | |
|---|
| 409 | wp_remote_post( $api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) ); |
|---|
| 410 | |
|---|
| 411 | }else{ |
|---|
| 412 | echo esc_html__('Email id required', 'wp-multilang'); |
|---|
| 413 | } |
|---|
| 414 | |
|---|
| 415 | wp_die(); |
|---|
| 416 | } |
|---|
| 417 | |
|---|
| 418 | /** |
|---|
| 419 | * Triggered when clicked on close button of newsletter form present in settings |
|---|
| 420 | * @since 2.4.7 |
|---|
| 421 | * */ |
|---|
| 422 | public static function newsletter_hide_form(){ |
|---|
| 423 | if(!current_user_can('manage_options')){ |
|---|
| 424 | wp_die( -1 ); |
|---|
| 425 | } |
|---|
| 426 | |
|---|
| 427 | if ( ! isset( $_POST['wpm_admin_settings_nonce'] ) ){ |
|---|
| 428 | wp_die( -1 ); |
|---|
| 429 | } |
|---|
| 430 | |
|---|
| 431 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 432 | if ( ! wp_verify_nonce( $_POST['wpm_admin_settings_nonce'], 'wpm_admin_settings_nonce' ) ) { |
|---|
| 433 | wp_die( -1 ); |
|---|
| 434 | } |
|---|
| 435 | |
|---|
| 436 | update_option( 'wpm_hide_newsletter', 'yes' , false); |
|---|
| 437 | |
|---|
| 438 | echo wp_json_encode(array('status'=>200, 'message'=>esc_html__('Submitted ','wp-multilang'))); |
|---|
| 439 | |
|---|
| 440 | wp_die(); |
|---|
| 441 | } |
|---|
| 442 | |
|---|
| 443 | /** |
|---|
| 444 | * Triggered when clicked on subscribe button of newsletter form present in settings |
|---|
| 445 | * @since 2.4.7 |
|---|
| 446 | * */ |
|---|
| 447 | public static function settings_newsletter_submit(){ |
|---|
| 448 | if(!current_user_can('manage_options')){ |
|---|
| 449 | wp_die( -1 ); |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| 452 | if ( ! isset( $_POST['wpm_admin_settings_nonce'] ) ){ |
|---|
| 453 | wp_die( -1 ); |
|---|
| 454 | } |
|---|
| 455 | |
|---|
| 456 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 457 | if ( !wp_verify_nonce( $_POST['wpm_admin_settings_nonce'], 'wpm_admin_settings_nonce' ) ){ |
|---|
| 458 | wp_die( -1 ); |
|---|
| 459 | } |
|---|
| 460 | |
|---|
| 461 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 462 | if ( isset ( $_POST['email'] ) && ! empty( $_POST['email'] ) ){ |
|---|
| 463 | global $current_user; |
|---|
| 464 | $api_url = 'http://magazine3.company/wp-json/api/central/email/subscribe'; |
|---|
| 465 | $api_params = array( |
|---|
| 466 | 'name' => sanitize_text_field($current_user->display_name), |
|---|
| 467 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 468 | 'email'=> sanitize_email( $_POST['email'] ), |
|---|
| 469 | 'website'=> sanitize_url( get_site_url() ), |
|---|
| 470 | 'type'=> 'wpmultilang' |
|---|
| 471 | ); |
|---|
| 472 | |
|---|
| 473 | $response = wp_remote_post( $api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) ); |
|---|
| 474 | if ( !is_wp_error( $response ) ) { |
|---|
| 475 | $response = wp_remote_retrieve_body( $response ); |
|---|
| 476 | echo wp_json_encode(array('status'=>200, 'message'=>esc_html__('Submitted ','wp-multilang'), 'response'=> $response)); |
|---|
| 477 | }else{ |
|---|
| 478 | echo wp_json_encode(array('status'=>500, 'message'=>esc_html__('No response from API','wp-multilang'))); |
|---|
| 479 | } |
|---|
| 480 | wp_die(); |
|---|
| 481 | } |
|---|
| 482 | } |
|---|
| 483 | |
|---|
| 484 | /** |
|---|
| 485 | * Prepare url to change the href of block language switcher |
|---|
| 486 | * @since 2.4.9 |
|---|
| 487 | * */ |
|---|
| 488 | public static function block_lang_switcher(){ |
|---|
| 489 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 490 | if ( ! isset( $_POST['security'] ) ){ |
|---|
| 491 | wp_die( -1 ); |
|---|
| 492 | } |
|---|
| 493 | |
|---|
| 494 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 495 | if ( !wp_verify_nonce( $_POST['security'], 'wpm_ajax_security_nonce' ) ) { |
|---|
| 496 | wp_die( -1 ); |
|---|
| 497 | } |
|---|
| 498 | |
|---|
| 499 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 500 | if( empty( $_POST['current_url'] ) ) { |
|---|
| 501 | wp_die( -1 ); |
|---|
| 502 | } |
|---|
| 503 | |
|---|
| 504 | $all_languages = wpm_get_languages(); |
|---|
| 505 | |
|---|
| 506 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Reason unslash not needed because data is not getting stored in database, it's just being used. |
|---|
| 507 | $current_url = sanitize_url($_POST['current_url']); |
|---|
| 508 | |
|---|
| 509 | $translated_urls = array(); |
|---|
| 510 | if(!empty($all_languages) && is_array($all_languages)){ |
|---|
| 511 | foreach ($all_languages as $al_key => $al_value) { |
|---|
| 512 | $translated_urls[$al_key] = wpm_translate_url( $current_url, $al_key ); |
|---|
| 513 | } |
|---|
| 514 | } |
|---|
| 515 | |
|---|
| 516 | echo wp_json_encode($translated_urls); |
|---|
| 517 | wp_die(); |
|---|
| 518 | } |
|---|
| 519 | |
|---|
| 520 | /** |
|---|
| 521 | * Reset plugin settings to default |
|---|
| 522 | * @since 2.4.15 |
|---|
| 523 | * */ |
|---|
| 524 | public static function reset_settings() { |
|---|
| 525 | |
|---|
| 526 | check_ajax_referer( 'wpm-reset-settings', 'security' ); |
|---|
| 527 | |
|---|
| 528 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 529 | wp_die( -1 ); |
|---|
| 530 | } |
|---|
| 531 | |
|---|
| 532 | $reset_obj = new WPM_Reset_Settings(); |
|---|
| 533 | $reset_obj->reset_settings(); |
|---|
| 534 | |
|---|
| 535 | wp_die(); |
|---|
| 536 | |
|---|
| 537 | } |
|---|
| 538 | |
|---|
| 539 | /** |
|---|
| 540 | * Validate API secret key |
|---|
| 541 | * @since 2.4.23 |
|---|
| 542 | * */ |
|---|
| 543 | public static function validate_secret_key() { |
|---|
| 544 | |
|---|
| 545 | check_ajax_referer( 'wpmpro-openai-nonce', 'security' ); |
|---|
| 546 | |
|---|
| 547 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 548 | wp_die( -1 ); |
|---|
| 549 | } |
|---|
| 550 | |
|---|
| 551 | try { |
|---|
| 552 | $result = WPM_OpenAI::validate_secret_key(); |
|---|
| 553 | if ( ! empty( $result['models'] ) ) { |
|---|
| 554 | $models = $result['models']; |
|---|
| 555 | $provider = $result['provider']; |
|---|
| 556 | |
|---|
| 557 | $api_settings = WPM_Settings_AI_Integration::get_openai_settings(); |
|---|
| 558 | $api_settings['api_keys'][$provider] = $result['api_key']; |
|---|
| 559 | $api_settings['api_provider'] = $provider; |
|---|
| 560 | $api_settings['api_available_models'][$provider] = $models; |
|---|
| 561 | |
|---|
| 562 | update_option( 'wpm_openai_settings', $api_settings ); |
|---|
| 563 | |
|---|
| 564 | wp_send_json_success( $result ); |
|---|
| 565 | } |
|---|
| 566 | |
|---|
| 567 | } catch (\Exception $e) { |
|---|
| 568 | wp_send_json_error(['message' => 'API Validation failed: ' . $e->getMessage()]); |
|---|
| 569 | } |
|---|
| 570 | |
|---|
| 571 | } |
|---|
| 572 | |
|---|
| 573 | /** |
|---|
| 574 | * Save openai settings |
|---|
| 575 | * @since 2.4.23 |
|---|
| 576 | * */ |
|---|
| 577 | public static function save_openai_settings() { |
|---|
| 578 | |
|---|
| 579 | check_ajax_referer( 'wpmpro-openai-nonce', 'security' ); |
|---|
| 580 | |
|---|
| 581 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 582 | wp_die( -1 ); |
|---|
| 583 | } |
|---|
| 584 | |
|---|
| 585 | WPM_Settings_AI_Integration::save_settings(); |
|---|
| 586 | } |
|---|
| 587 | |
|---|
| 588 | |
|---|
| 589 | /** |
|---|
| 590 | * Process batch translation |
|---|
| 591 | * @since 1.10 |
|---|
| 592 | * */ |
|---|
| 593 | public static function process_batch_translation(){ |
|---|
| 594 | |
|---|
| 595 | if ( ! wp_verify_nonce( $_POST['wpmpro_autotranslate_singular_nonce'], 'wpmpro-autotranslate-singular-nonce' ) ) { |
|---|
| 596 | wp_send_json_error( array( 'message' => esc_html__( 'security nonce not verify', 'wp-multilang' ) ) ); |
|---|
| 597 | } |
|---|
| 598 | |
|---|
| 599 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 600 | wp_send_json_error( array( 'message' => esc_html__( 'not authorized', 'wp-multilang' ) ) ); |
|---|
| 601 | } |
|---|
| 602 | |
|---|
| 603 | if ( ! empty( $_POST['post_id'] ) && ! empty( $_POST['source'] ) && ! empty( $_POST['target'] ) && isset( $_POST['batch_start'] ) && isset( $_POST['batch_size'] ) ) { |
|---|
| 604 | |
|---|
| 605 | $post_id = intval( $_POST['post_id'] ); |
|---|
| 606 | $batch_start = intval( $_POST['batch_start'] ); |
|---|
| 607 | $batch_size = intval( $_POST['batch_size'] ); |
|---|
| 608 | |
|---|
| 609 | if ( $post_id <= 0 ) { |
|---|
| 610 | wp_send_json_error( array( 'message' => esc_html__( 'Not a valid post', 'wp-multilang' ) ) ); |
|---|
| 611 | } |
|---|
| 612 | |
|---|
| 613 | $source = sanitize_text_field( wp_unslash( $_POST['source'] ) ); |
|---|
| 614 | $target = sanitize_text_field( wp_unslash( $_POST['target'] ) ); |
|---|
| 615 | $post = get_post( $post_id ); |
|---|
| 616 | |
|---|
| 617 | if ( ! $post ) { |
|---|
| 618 | wp_send_json_error( array( 'message' => esc_html__( 'Post not found', 'wp-multilang' ) ) ); |
|---|
| 619 | } |
|---|
| 620 | |
|---|
| 621 | $response = WPM_Settings_Auto_Translate_Pro::auto_translate_batch( $post, $source, $target, $batch_start, $batch_size ); |
|---|
| 622 | |
|---|
| 623 | wp_send_json( $response ); |
|---|
| 624 | } else { |
|---|
| 625 | wp_send_json_error( array( 'message' => esc_html__( 'Missing required parameters', 'wp-multilang' ) ) ); |
|---|
| 626 | } |
|---|
| 627 | } |
|---|
| 628 | |
|---|
| 629 | /** |
|---|
| 630 | * Auto translate single post, product, page custom post types |
|---|
| 631 | * @since 1.10 |
|---|
| 632 | * */ |
|---|
| 633 | public static function singlular_auto_translate(){ |
|---|
| 634 | |
|---|
| 635 | // Set longer execution time and prevent timeouts |
|---|
| 636 | set_time_limit(1200); // Increased to 20 minutes |
|---|
| 637 | ini_set('max_input_time', 1200); |
|---|
| 638 | ini_set('default_socket_timeout', 120); |
|---|
| 639 | |
|---|
| 640 | // Send headers to prevent browser timeout |
|---|
| 641 | header('Content-Type: application/json'); |
|---|
| 642 | header('Cache-Control: no-cache, must-revalidate'); |
|---|
| 643 | header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); |
|---|
| 644 | |
|---|
| 645 | |
|---|
| 646 | if ( ! wp_verify_nonce( $_POST['wpmpro_autotranslate_singular_nonce'], 'wpmpro-autotranslate-singular-nonce' ) ) { |
|---|
| 647 | echo esc_html__( 'security nonce not verify', 'wp-multilang' ); |
|---|
| 648 | wp_die(); |
|---|
| 649 | } |
|---|
| 650 | |
|---|
| 651 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 652 | echo esc_html__( 'not authorized', 'wp-multilang' ); |
|---|
| 653 | wp_die(); |
|---|
| 654 | } |
|---|
| 655 | |
|---|
| 656 | if ( ! empty( $_POST['post_id'] ) && ! empty( $_POST['source'] ) && ! empty( $_POST['target'] ) ) { |
|---|
| 657 | |
|---|
| 658 | $post_id = intval( $_POST['post_id'] ); |
|---|
| 659 | if ( $post_id <= 0 ) { |
|---|
| 660 | echo esc_html__( 'Not a valid post', 'wp-multilang' ); |
|---|
| 661 | wp_die(); |
|---|
| 662 | } |
|---|
| 663 | |
|---|
| 664 | $source = sanitize_text_field( wp_unslash( $_POST['source'] ) ); |
|---|
| 665 | $target = sanitize_text_field( wp_unslash( $_POST['target'] ) ); |
|---|
| 666 | $post = get_post( $post_id ); |
|---|
| 667 | $post_type = get_post_type( $post_id ); |
|---|
| 668 | $response = array(); |
|---|
| 669 | |
|---|
| 670 | // DEBUG: Start comprehensive debugging |
|---|
| 671 | $debug_log = array(); |
|---|
| 672 | $debug_log['timestamp'] = current_time('mysql'); |
|---|
| 673 | $debug_log['post_id'] = $post_id; |
|---|
| 674 | $debug_log['post_type'] = $post_type; |
|---|
| 675 | $debug_log['source_lang'] = $source; |
|---|
| 676 | $debug_log['target_lang'] = $target; |
|---|
| 677 | $debug_log['post_title'] = $post->post_title; |
|---|
| 678 | $debug_log['post_content_length'] = strlen($post->post_content); |
|---|
| 679 | |
|---|
| 680 | // DEBUG: Check Elementor data |
|---|
| 681 | $elementor_data = get_post_meta($post_id, '_elementor_data', true); |
|---|
| 682 | $debug_log['has_elementor_data'] = !empty($elementor_data); |
|---|
| 683 | $debug_log['elementor_data_length'] = $elementor_data ? strlen($elementor_data) : 0; |
|---|
| 684 | |
|---|
| 685 | // DEBUG: Check Elementor translate meta |
|---|
| 686 | $elementor_translate = get_post_meta($post_id, '_elementor_data_translate', true); |
|---|
| 687 | $debug_log['has_elementor_translate'] = !empty($elementor_translate); |
|---|
| 688 | $debug_log['elementor_translate_length'] = $elementor_translate ? strlen($elementor_translate) : 0; |
|---|
| 689 | |
|---|
| 690 | // DEBUG: Check for Rank Math meta fields |
|---|
| 691 | $rank_math_metas = array(); |
|---|
| 692 | $all_metas = get_post_meta($post_id); |
|---|
| 693 | foreach ($all_metas as $meta_key => $meta_value) { |
|---|
| 694 | if (strpos($meta_key, 'rank_math') === 0) { |
|---|
| 695 | $rank_math_metas[$meta_key] = is_array($meta_value) ? $meta_value[0] : $meta_value; |
|---|
| 696 | } |
|---|
| 697 | } |
|---|
| 698 | $debug_log['rank_math_metas'] = $rank_math_metas; |
|---|
| 699 | $debug_log['rank_math_meta_count'] = count($rank_math_metas); |
|---|
| 700 | |
|---|
| 701 | // DEBUG: Check for other SEO meta fields |
|---|
| 702 | $seo_metas = array(); |
|---|
| 703 | foreach ($all_metas as $meta_key => $meta_value) { |
|---|
| 704 | if (strpos($meta_key, '_yoast') === 0 || strpos($meta_key, 'rank_math') === 0 || strpos($meta_key, 'seopress') === 0) { |
|---|
| 705 | $seo_metas[$meta_key] = is_array($meta_value) ? $meta_value[0] : $meta_value; |
|---|
| 706 | } |
|---|
| 707 | } |
|---|
| 708 | $debug_log['seo_metas'] = $seo_metas; |
|---|
| 709 | $debug_log['seo_meta_count'] = count($seo_metas); |
|---|
| 710 | |
|---|
| 711 | // DEBUG: Check if Elementor is active |
|---|
| 712 | $debug_log['elementor_active'] = class_exists('\Elementor\Plugin'); |
|---|
| 713 | $debug_log['elementor_version'] = defined('ELEMENTOR_VERSION') ? ELEMENTOR_VERSION : 'Not defined'; |
|---|
| 714 | |
|---|
| 715 | // DEBUG: Check if Rank Math is active |
|---|
| 716 | $debug_log['rank_math_active'] = defined('RANK_MATH_VERSION'); |
|---|
| 717 | $debug_log['rank_math_version'] = defined('RANK_MATH_VERSION') ? RANK_MATH_VERSION : 'Not defined'; |
|---|
| 718 | |
|---|
| 719 | // DEBUG: Log current post meta structure |
|---|
| 720 | $debug_log['all_meta_keys'] = array_keys($all_metas); |
|---|
| 721 | $debug_log['meta_keys_count'] = count($all_metas); |
|---|
| 722 | |
|---|
| 723 | try { |
|---|
| 724 | switch( $post_type ) { |
|---|
| 725 | |
|---|
| 726 | case 'saswp_reviews': |
|---|
| 727 | $response = \WPM\Includes\Integrations\WPM_Schema_Saswp::auto_translate( $post, $source, $target ); |
|---|
| 728 | break; |
|---|
| 729 | |
|---|
| 730 | default: |
|---|
| 731 | $response = WPM_Settings_Auto_Translate_Pro::auto_translate( $post, $source, $target , true); |
|---|
| 732 | break; |
|---|
| 733 | |
|---|
| 734 | } |
|---|
| 735 | } catch (Exception $e) { |
|---|
| 736 | $response = array('status' => false, 'message' => 'Translation failed: ' . $e->getMessage()); |
|---|
| 737 | $debug_log['exception'] = $e->getMessage(); |
|---|
| 738 | } |
|---|
| 739 | |
|---|
| 740 | // DEBUG: Log response details |
|---|
| 741 | $debug_log['response'] = $response; |
|---|
| 742 | |
|---|
| 743 | // DEBUG: Check post meta after translation |
|---|
| 744 | $post_meta_after = get_post_meta($post_id); |
|---|
| 745 | $elementor_data_after = get_post_meta($post_id, '_elementor_data', true); |
|---|
| 746 | $elementor_translate_after = get_post_meta($post_id, '_elementor_data_translate', true); |
|---|
| 747 | |
|---|
| 748 | $debug_log['elementor_data_after_length'] = $elementor_data_after ? strlen($elementor_data_after) : 0; |
|---|
| 749 | $debug_log['elementor_translate_after_length'] = $elementor_translate_after ? strlen($elementor_translate_after) : 0; |
|---|
| 750 | $debug_log['elementor_data_changed'] = ($elementor_data !== $elementor_data_after); |
|---|
| 751 | $debug_log['elementor_translate_changed'] = ($elementor_translate !== $elementor_translate_after); |
|---|
| 752 | |
|---|
| 753 | // DEBUG: Check rank math metas after translation |
|---|
| 754 | $rank_math_metas_after = array(); |
|---|
| 755 | foreach ($post_meta_after as $meta_key => $meta_value) { |
|---|
| 756 | if (strpos($meta_key, 'rank_math') === 0) { |
|---|
| 757 | $rank_math_metas_after[$meta_key] = is_array($meta_value) ? $meta_value[0] : $meta_value; |
|---|
| 758 | } |
|---|
| 759 | } |
|---|
| 760 | $debug_log['rank_math_metas_after'] = $rank_math_metas_after; |
|---|
| 761 | $debug_log['rank_math_metas_changed'] = ($rank_math_metas !== $rank_math_metas_after); |
|---|
| 762 | |
|---|
| 763 | // DEBUG: Write debug log to file |
|---|
| 764 | $debug_file = WP_CONTENT_DIR . '/wpm_debug_translation.log'; |
|---|
| 765 | $debug_entry = "=== TRANSLATION DEBUG - " . current_time('Y-m-d H:i:s') . " ===\n"; |
|---|
| 766 | $debug_entry .= "Post ID: {$post_id} | Type: {$post_type} | Source: {$source} | Target: {$target}\n"; |
|---|
| 767 | $debug_entry .= "Elementor Active: " . ($debug_log['elementor_active'] ? 'Yes' : 'No') . " | Version: " . $debug_log['elementor_version'] . "\n"; |
|---|
| 768 | $debug_entry .= "Rank Math Active: " . ($debug_log['rank_math_active'] ? 'Yes' : 'No') . " | Version: " . $debug_log['rank_math_version'] . "\n"; |
|---|
| 769 | $debug_entry .= "Has Elementor Data: " . ($debug_log['has_elementor_data'] ? 'Yes' : 'No') . " | Length: " . $debug_log['elementor_data_length'] . "\n"; |
|---|
| 770 | $debug_entry .= "Has Elementor Translate: " . ($debug_log['has_elementor_translate'] ? 'Yes' : 'No') . " | Length: " . $debug_log['elementor_translate_length'] . "\n"; |
|---|
| 771 | $debug_entry .= "Rank Math Meta Count: " . $debug_log['rank_math_meta_count'] . "\n"; |
|---|
| 772 | $debug_entry .= "SEO Meta Count: " . $debug_log['seo_meta_count'] . "\n"; |
|---|
| 773 | $debug_entry .= "Elementor Data Changed: " . ($debug_log['elementor_data_changed'] ? 'Yes' : 'No') . "\n"; |
|---|
| 774 | $debug_entry .= "Elementor Translate Changed: " . ($debug_log['elementor_translate_changed'] ? 'Yes' : 'No') . "\n"; |
|---|
| 775 | $debug_entry .= "Rank Math Metas Changed: " . ($debug_log['rank_math_metas_changed'] ? 'Yes' : 'No') . "\n"; |
|---|
| 776 | $debug_entry .= "Note: rank_math_schema_Service is excluded from translation to prevent serialization errors\n"; |
|---|
| 777 | $debug_entry .= "Note: rank_math_title and rank_math_description are translated directly in original meta fields\n"; |
|---|
| 778 | $debug_entry .= "Response: " . json_encode($response) . "\n"; |
|---|
| 779 | $debug_entry .= "All Meta Keys: " . implode(', ', $debug_log['all_meta_keys']) . "\n"; |
|---|
| 780 | $debug_entry .= "Rank Math Metas: " . json_encode($rank_math_metas) . "\n"; |
|---|
| 781 | $debug_entry .= "Rank Math Metas After: " . json_encode($rank_math_metas_after) . "\n"; |
|---|
| 782 | $debug_entry .= "=== END DEBUG ===\n\n"; |
|---|
| 783 | |
|---|
| 784 | file_put_contents($debug_file, $debug_entry, FILE_APPEND | LOCK_EX); |
|---|
| 785 | |
|---|
| 786 | // Ensure we have a valid response |
|---|
| 787 | if (empty($response) || !is_array($response)) { |
|---|
| 788 | $response = array('status' => false, 'message' => 'Translation completed but no response generated'); |
|---|
| 789 | } |
|---|
| 790 | |
|---|
| 791 | // Ensure we're sending proper JSON |
|---|
| 792 | header('Content-Type: application/json'); |
|---|
| 793 | wp_send_json($response); |
|---|
| 794 | wp_die(); |
|---|
| 795 | } |
|---|
| 796 | |
|---|
| 797 | } |
|---|
| 798 | |
|---|
| 799 | public static function singlular_auto_translate_term(){ |
|---|
| 800 | |
|---|
| 801 | if ( ! wp_verify_nonce( $_POST['wpmpro_autotranslate_singular_nonce'], 'wpmpro-autotranslate-singular-nonce' ) ) { |
|---|
| 802 | echo esc_html__( 'security nonce not verify', 'wp-multilang' ); |
|---|
| 803 | wp_die(); |
|---|
| 804 | } |
|---|
| 805 | |
|---|
| 806 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 807 | echo esc_html__( 'not authorized', 'wp-multilang' ); |
|---|
| 808 | wp_die(); |
|---|
| 809 | } |
|---|
| 810 | |
|---|
| 811 | if ( ! empty( $_POST['tag_id'] ) && ! empty( $_POST['source'] ) && ! empty( $_POST['target'] ) ) { |
|---|
| 812 | |
|---|
| 813 | global $wpdb; |
|---|
| 814 | |
|---|
| 815 | $tag_id = intval( $_POST['tag_id'] ); |
|---|
| 816 | if ( $tag_id <= 0 ) { |
|---|
| 817 | echo esc_html__( 'Not a vlid post', 'wp-multilang' ); |
|---|
| 818 | wp_die(); |
|---|
| 819 | } |
|---|
| 820 | |
|---|
| 821 | $source = sanitize_text_field( wp_unslash( $_POST['source'] ) ); |
|---|
| 822 | $target = sanitize_text_field( wp_unslash( $_POST['target'] ) ); |
|---|
| 823 | |
|---|
| 824 | $query = $wpdb->prepare(" |
|---|
| 825 | SELECT tt.*, t.* |
|---|
| 826 | FROM {$wpdb->term_taxonomy} AS tt |
|---|
| 827 | INNER JOIN {$wpdb->terms} AS t ON tt.term_id = t.term_id |
|---|
| 828 | WHERE tt.term_id = %d |
|---|
| 829 | ", $tag_id |
|---|
| 830 | ); |
|---|
| 831 | $term = $wpdb->get_row( $query ); |
|---|
| 832 | if ( is_object( $term ) && ! empty( $term ) ) { |
|---|
| 833 | $response = WPM_Settings_Auto_Translate_Pro::auto_translate_term( $term, $source, $target, true ); |
|---|
| 834 | } |
|---|
| 835 | |
|---|
| 836 | wp_send_json($response); |
|---|
| 837 | wp_die(); |
|---|
| 838 | } |
|---|
| 839 | } |
|---|
| 840 | |
|---|
| 841 | /** |
|---|
| 842 | * Auto translate single post, product, page custom post types |
|---|
| 843 | * @since 1.10 |
|---|
| 844 | * */ |
|---|
| 845 | /** |
|---|
| 846 | * Search items for exclusion list |
|---|
| 847 | */ |
|---|
| 848 | public static function wpmpro_search_items() { |
|---|
| 849 | // Check nonce - handle both _wpnonce and nonce parameters |
|---|
| 850 | $nonce = isset($_REQUEST['_wpnonce']) ? $_REQUEST['_wpnonce'] : (isset($_REQUEST['nonce']) ? $_REQUEST['nonce'] : ''); |
|---|
| 851 | if (!wp_verify_nonce($nonce, 'wpmpro_search_items')) { |
|---|
| 852 | wp_send_json_error('Invalid nonce'); |
|---|
| 853 | } |
|---|
| 854 | |
|---|
| 855 | if (!current_user_can('manage_options')) { |
|---|
| 856 | wp_send_json_error('Unauthorized'); |
|---|
| 857 | } |
|---|
| 858 | |
|---|
| 859 | $search = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : ''; |
|---|
| 860 | $type = isset($_GET['type']) ? sanitize_text_field($_GET['type']) : ''; |
|---|
| 861 | $results = array(); |
|---|
| 862 | |
|---|
| 863 | switch ($type) { |
|---|
| 864 | case 'post': |
|---|
| 865 | case 'page': |
|---|
| 866 | case 'product': |
|---|
| 867 | $args = array( |
|---|
| 868 | 'post_type' => $type, |
|---|
| 869 | 'post_status' => 'publish', |
|---|
| 870 | 's' => $search, |
|---|
| 871 | 'posts_per_page' => 10 |
|---|
| 872 | ); |
|---|
| 873 | $query = new \WP_Query($args); |
|---|
| 874 | foreach ($query->posts as $post) { |
|---|
| 875 | $results[] = array( |
|---|
| 876 | 'id' => $post->ID, |
|---|
| 877 | 'text' => $post->post_title |
|---|
| 878 | ); |
|---|
| 879 | } |
|---|
| 880 | break; |
|---|
| 881 | |
|---|
| 882 | case 'category': |
|---|
| 883 | case 'post_tag': |
|---|
| 884 | case 'product_cat': |
|---|
| 885 | $args = array( |
|---|
| 886 | 'taxonomy' => $type, |
|---|
| 887 | 'hide_empty' => false, |
|---|
| 888 | 'search' => $search, |
|---|
| 889 | 'number' => 10 |
|---|
| 890 | ); |
|---|
| 891 | $terms = get_terms($args); |
|---|
| 892 | if (!is_wp_error($terms)) { |
|---|
| 893 | foreach ($terms as $term) { |
|---|
| 894 | $results[] = array( |
|---|
| 895 | 'id' => $term->term_id, |
|---|
| 896 | 'text' => $term->name |
|---|
| 897 | ); |
|---|
| 898 | } |
|---|
| 899 | } |
|---|
| 900 | break; |
|---|
| 901 | } |
|---|
| 902 | |
|---|
| 903 | wp_send_json($results); |
|---|
| 904 | } |
|---|
| 905 | |
|---|
| 906 | /** |
|---|
| 907 | * Get total node count for batch translation |
|---|
| 908 | * @since 1.10 |
|---|
| 909 | * */ |
|---|
| 910 | public static function get_translation_node_count(){ |
|---|
| 911 | |
|---|
| 912 | if ( ! wp_verify_nonce( $_POST['wpmpro_autotranslate_singular_nonce'], 'wpmpro-autotranslate-singular-nonce' ) ) { |
|---|
| 913 | wp_send_json_error( array( 'message' => esc_html__( 'security nonce not verify', 'wp-multilang' ) ) ); |
|---|
| 914 | } |
|---|
| 915 | |
|---|
| 916 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 917 | wp_send_json_error( array( 'message' => esc_html__( 'not authorized', 'wp-multilang' ) ) ); |
|---|
| 918 | } |
|---|
| 919 | |
|---|
| 920 | if ( ! empty( $_POST['post_id'] ) && ! empty( $_POST['source'] ) && ! empty( $_POST['target'] ) ) { |
|---|
| 921 | |
|---|
| 922 | $post_id = intval( $_POST['post_id'] ); |
|---|
| 923 | if ( $post_id <= 0 ) { |
|---|
| 924 | wp_send_json_error( array( 'message' => esc_html__( 'Not a valid post', 'wp-multilang' ) ) ); |
|---|
| 925 | } |
|---|
| 926 | |
|---|
| 927 | $source = sanitize_text_field( wp_unslash( $_POST['source'] ) ); |
|---|
| 928 | $target = sanitize_text_field( wp_unslash( $_POST['target'] ) ); |
|---|
| 929 | $post = get_post( $post_id ); |
|---|
| 930 | |
|---|
| 931 | if ( ! $post ) { |
|---|
| 932 | wp_send_json_error( array( 'message' => esc_html__( 'Post not found', 'wp-multilang' ) ) ); |
|---|
| 933 | } |
|---|
| 934 | |
|---|
| 935 | // Get source content |
|---|
| 936 | $post_title = $post->post_title; |
|---|
| 937 | $post_content = $post->post_content; |
|---|
| 938 | $post_excerpt = $post->post_excerpt; |
|---|
| 939 | |
|---|
| 940 | $is_title_exist = wpm_ml_check_language_string( $post_title, $target ); |
|---|
| 941 | $is_content_exist = wpm_ml_check_language_string( $post_content, $target ); |
|---|
| 942 | $is_excerpt_exist = wpm_ml_check_language_string( $post_excerpt, $target ); |
|---|
| 943 | |
|---|
| 944 | $total_nodes = 0; |
|---|
| 945 | |
|---|
| 946 | // Count nodes in title |
|---|
| 947 | if ( $is_title_exist === false ) { |
|---|
| 948 | $is_src_title_exist = wpm_ml_check_language_string( $post_title, $source ); |
|---|
| 949 | if( $is_src_title_exist === false ) { |
|---|
| 950 | $post_title = '[:'.$source.']'.$post_title.'[:]'; |
|---|
| 951 | } |
|---|
| 952 | $source_title = wpm_ml_get_language_string( $post_title, $source ); |
|---|
| 953 | $title_nodes = wpm_ml_auto_translate_content( $source_title, $source, $target, 0, 0 ); |
|---|
| 954 | if ( is_array( $title_nodes ) && isset( $title_nodes['total_nodes'] ) ) { |
|---|
| 955 | $total_nodes += $title_nodes['total_nodes']; |
|---|
| 956 | } |
|---|
| 957 | } |
|---|
| 958 | |
|---|
| 959 | // Count nodes in content |
|---|
| 960 | if ( $is_content_exist === false ) { |
|---|
| 961 | $is_src_content_exist = wpm_ml_check_language_string( $post_content, $source ); |
|---|
| 962 | if ( $is_src_content_exist === false ) { |
|---|
| 963 | $post_content = '[:'.$source.']'.$post_content.'[:]'; |
|---|
| 964 | } |
|---|
| 965 | $source_content = wpm_ml_get_language_string( $post_content, $source ); |
|---|
| 966 | $content_nodes = wpm_ml_auto_translate_content( $source_content, $source, $target, 0, 0 ); |
|---|
| 967 | if ( is_array( $content_nodes ) && isset( $content_nodes['total_nodes'] ) ) { |
|---|
| 968 | $total_nodes += $content_nodes['total_nodes']; |
|---|
| 969 | } |
|---|
| 970 | } |
|---|
| 971 | |
|---|
| 972 | // Count nodes in excerpt |
|---|
| 973 | if ( $is_excerpt_exist === false && $post_excerpt ) { |
|---|
| 974 | $is_src_excerpt_exist = wpm_ml_check_language_string( $post_excerpt, $source ); |
|---|
| 975 | if ( $is_src_excerpt_exist === false ) { |
|---|
| 976 | $post_excerpt = '[:'.$source.']'.$post_excerpt.'[:]'; |
|---|
| 977 | } |
|---|
| 978 | $source_excerpt = wpm_ml_get_language_string( $post_excerpt, $source ); |
|---|
| 979 | $excerpt_nodes = wpm_ml_auto_translate_content( $source_excerpt, $source, $target, 0, 0 ); |
|---|
| 980 | if ( is_array( $excerpt_nodes ) && isset( $excerpt_nodes['total_nodes'] ) ) { |
|---|
| 981 | $total_nodes += $excerpt_nodes['total_nodes']; |
|---|
| 982 | } |
|---|
| 983 | } |
|---|
| 984 | |
|---|
| 985 | wp_send_json_success( array( |
|---|
| 986 | 'total_nodes' => $total_nodes, |
|---|
| 987 | 'message' => sprintf( esc_html__( 'Found %d text nodes to translate', 'wp-multilang' ), $total_nodes ) |
|---|
| 988 | ) ); |
|---|
| 989 | } else { |
|---|
| 990 | wp_send_json_error( array( 'message' => esc_html__( 'Missing required parameters', 'wp-multilang' ) ) ); |
|---|
| 991 | } |
|---|
| 992 | } |
|---|
| 993 | |
|---|
| 994 | /** |
|---|
| 995 | * Auto Translation |
|---|
| 996 | * @since 1.4 |
|---|
| 997 | * */ |
|---|
| 998 | public static function do_auto_translate() { |
|---|
| 999 | |
|---|
| 1000 | // Set longer execution time and prevent timeouts |
|---|
| 1001 | set_time_limit(120); // 2 minutes |
|---|
| 1002 | ini_set('max_input_time', 120); |
|---|
| 1003 | ini_set('default_socket_timeout', 120); |
|---|
| 1004 | |
|---|
| 1005 | // Send headers to prevent browser timeout |
|---|
| 1006 | header('Content-Type: application/json'); |
|---|
| 1007 | header('Cache-Control: no-cache, must-revalidate'); |
|---|
| 1008 | header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); |
|---|
| 1009 | |
|---|
| 1010 | if ( ! wp_verify_nonce( $_POST['wpmpro_autotranslate_nonce'], 'wpmpro-autotranslate-nonce' ) ) { |
|---|
| 1011 | wp_send_json_error( array( 'message' => esc_html__( 'security nonce not verify', 'wp-multilang' ) ) ); |
|---|
| 1012 | } |
|---|
| 1013 | |
|---|
| 1014 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 1015 | wp_send_json_error( array( 'message' => esc_html__( 'not authorized', 'wp-multilang' ) ) ); |
|---|
| 1016 | } |
|---|
| 1017 | |
|---|
| 1018 | if ( ! isset( $_POST['post_type'] ) || ! isset( $_POST['offset'] ) || ! isset( $_POST['source'] ) || ! isset( $_POST['target'] ) ) { |
|---|
| 1019 | wp_send_json_error( array( 'message' => esc_html__( 'Some parameters are missing', 'wp-multilang' ) ) ); |
|---|
| 1020 | } |
|---|
| 1021 | |
|---|
| 1022 | $post_type = sanitize_text_field( $_POST['post_type'] ); |
|---|
| 1023 | $offset = intval( $_POST['offset'] ) - 1; |
|---|
| 1024 | $source = sanitize_text_field( $_POST['source'] ); |
|---|
| 1025 | $target = sanitize_text_field( $_POST['target'] ); |
|---|
| 1026 | $response = array('status'=>false, 'message'=>esc_html__('No content found to translate','wp-multilang')); |
|---|
| 1027 | $category_array = array( 'category', 'post_tag', 'product_cat' ); // Supported categories |
|---|
| 1028 | |
|---|
| 1029 | try { |
|---|
| 1030 | if ( ! in_array( $post_type, $category_array ) ) { |
|---|
| 1031 | $posts = get_posts( array( |
|---|
| 1032 | 'post_type' => $post_type, |
|---|
| 1033 | 'post_status' => 'publish', |
|---|
| 1034 | 'posts_per_page' => 1, |
|---|
| 1035 | 'offset' => $offset, |
|---|
| 1036 | 'orderby' => 'ID', |
|---|
| 1037 | 'order' => 'ASC', |
|---|
| 1038 | ) |
|---|
| 1039 | ); |
|---|
| 1040 | |
|---|
| 1041 | if ( empty( $posts ) ) { |
|---|
| 1042 | $response = array('status'=>true, 'message'=>esc_html__('No posts found at this offset - continuing to next','wp-multilang')); |
|---|
| 1043 | } else { |
|---|
| 1044 | foreach($posts as $post) { |
|---|
| 1045 | if ( $post && isset($post->ID) ) { |
|---|
| 1046 | // Bulk auto-translate should NOT override existing translations |
|---|
| 1047 | $override = false; |
|---|
| 1048 | $response = WPM_Settings_Auto_Translate_Pro::auto_translate( $post, $source, $target, $override ); |
|---|
| 1049 | break; // Only process one post per request |
|---|
| 1050 | } |
|---|
| 1051 | } |
|---|
| 1052 | |
|---|
| 1053 | // Ensure we always have a response |
|---|
| 1054 | if ( !isset($response) || empty($response) ) { |
|---|
| 1055 | $response = array('status'=>true, 'message'=>esc_html__('Post processed but no translation needed','wp-multilang')); |
|---|
| 1056 | } |
|---|
| 1057 | } |
|---|
| 1058 | } else if ( in_array( $post_type, $category_array ) ) { |
|---|
| 1059 | |
|---|
| 1060 | global $wpdb; |
|---|
| 1061 | |
|---|
| 1062 | $terms = get_terms( |
|---|
| 1063 | array( |
|---|
| 1064 | 'taxonomy' => $post_type, |
|---|
| 1065 | 'hide_empty' => false, // Set to true if you only want tags that are assigned to posts / products |
|---|
| 1066 | 'number' => 1, |
|---|
| 1067 | 'offset' => $offset, |
|---|
| 1068 | 'orderby' => 'term_id', |
|---|
| 1069 | 'order' => 'ASC' |
|---|
| 1070 | ) |
|---|
| 1071 | ); |
|---|
| 1072 | if ( ! empty( $terms ) && is_array( $terms ) ) { |
|---|
| 1073 | foreach ( $terms as $term ) { |
|---|
| 1074 | if ( is_object( $term ) && isset( $term->term_id ) ) { |
|---|
| 1075 | |
|---|
| 1076 | // Here we could have passed $term as a parameter but this has translated name, so auto_translate_term function needs raw name and description so writing manual query is necessary here |
|---|
| 1077 | $query = $wpdb->prepare(" |
|---|
| 1078 | SELECT tt.*, t.* |
|---|
| 1079 | FROM {$wpdb->term_taxonomy} AS tt |
|---|
| 1080 | INNER JOIN {$wpdb->terms} AS t ON tt.term_id = t.term_id |
|---|
| 1081 | WHERE tt.term_id = %d |
|---|
| 1082 | ", $term->term_id |
|---|
| 1083 | ); |
|---|
| 1084 | $raw_term = $wpdb->get_row( $query ); |
|---|
| 1085 | |
|---|
| 1086 | if ( is_object( $raw_term ) && isset( $raw_term->term_id ) ) { |
|---|
| 1087 | // Bulk auto-translate should NOT override existing translations |
|---|
| 1088 | $override = false; |
|---|
| 1089 | $response = WPM_Settings_Auto_Translate_Pro::auto_translate_term( $raw_term, $source, $target, $override ); |
|---|
| 1090 | break; // Only process one term per request |
|---|
| 1091 | } |
|---|
| 1092 | } |
|---|
| 1093 | } |
|---|
| 1094 | |
|---|
| 1095 | // Ensure we always have a response |
|---|
| 1096 | if ( !isset($response) || empty($response) ) { |
|---|
| 1097 | $response = array('status'=>true, 'message'=>esc_html__('Term processed but no translation needed','wp-multilang')); |
|---|
| 1098 | } |
|---|
| 1099 | } else { |
|---|
| 1100 | $response = array('status'=>true, 'message'=>esc_html__('No terms found at this offset - continuing to next','wp-multilang')); |
|---|
| 1101 | } |
|---|
| 1102 | |
|---|
| 1103 | } |
|---|
| 1104 | } catch ( Exception $e ) { |
|---|
| 1105 | $response = array('status'=>false, 'message'=>esc_html__('Translation failed: ','wp-multilang') . $e->getMessage()); |
|---|
| 1106 | } |
|---|
| 1107 | |
|---|
| 1108 | // Ensure response is always valid |
|---|
| 1109 | if ( !isset($response) || !is_array($response) ) { |
|---|
| 1110 | $response = array('status'=>true, 'message'=>esc_html__('Request processed successfully','wp-multilang')); |
|---|
| 1111 | } |
|---|
| 1112 | |
|---|
| 1113 | // Add debugging information |
|---|
| 1114 | $response['debug'] = array( |
|---|
| 1115 | 'post_type' => $post_type, |
|---|
| 1116 | 'offset' => $offset + 1, |
|---|
| 1117 | 'source' => $source, |
|---|
| 1118 | 'target' => $target, |
|---|
| 1119 | 'override' => false, // Bulk auto-translate never overrides existing translations |
|---|
| 1120 | 'timestamp' => current_time('mysql') |
|---|
| 1121 | ); |
|---|
| 1122 | |
|---|
| 1123 | wp_send_json($response); |
|---|
| 1124 | } |
|---|
| 1125 | |
|---|
| 1126 | /** |
|---|
| 1127 | * Check ai platform api key quota |
|---|
| 1128 | * @since 2.4.23 |
|---|
| 1129 | * */ |
|---|
| 1130 | public static function check_ai_platform_quota() { |
|---|
| 1131 | |
|---|
| 1132 | if ( ! wp_verify_nonce( $_POST['wpmpro_autotranslate_nonce'], 'wpmpro-autotranslate-nonce' ) ) { |
|---|
| 1133 | wp_send_json_error( array( 'message' => esc_html__( 'security nonce not verify', 'wp-multilang' ) ) ); |
|---|
| 1134 | } |
|---|
| 1135 | |
|---|
| 1136 | if ( ! current_user_can( 'manage_options' ) ) { |
|---|
| 1137 | wp_send_json_error( array( 'message' => esc_html__( 'not authorized', 'wp-multilang' ) ) ); |
|---|
| 1138 | } |
|---|
| 1139 | |
|---|
| 1140 | $response = WPM_Settings_AI_Integration::check_ai_platform_quota(); |
|---|
| 1141 | |
|---|
| 1142 | wp_send_json($response); |
|---|
| 1143 | } |
|---|
| 1144 | } |
|---|