Plugin Directory

source: wp-multilang/trunk/includes/class-wpm-ajax.php

Last change on this file was 3414013, checked in by magazine3, 5 weeks ago

Released version 2.4.24

File size: 41.8 KB
Line 
1<?php
2
3namespace WPM\Includes;
4use WPM\Includes\Admin\WPM_Reset_Settings;
5use WPM\Includes\Admin\WPM_OpenAI;
6use WPM\Includes\Admin\Settings\WPM_Settings_Auto_Translate_Pro;
7use WPM\Includes\Admin\Settings\WPM_Settings_AI_Integration;
8
9if ( ! 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 */
23class 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}
Note: See TracBrowser for help on using the repository browser.