HEX
Server: nginx/1.18.0
System: Linux test-ipsremont 5.4.0-214-generic #234-Ubuntu SMP Fri Mar 14 23:50:27 UTC 2025 x86_64
User: ips (1000)
PHP: 8.0.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/quadcodewordpressapi/public/wp-content/plugins/transtations/cron.php
<?php
ignore_user_abort(true);
$_SERVER['HTTP_HOST'] = '';
if (!headers_sent()) {
    header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
    header('Cache-Control: no-cache, must-revalidate, max-age=0');
}
define('DOING_CRON', true);

if (!defined('ABSPATH')) {
    /** Set up WordPress environment */
    require_once __DIR__ . '/../../../wp-load.php';
}

use Dotenv\Dotenv;

function translateWbPosts()
{
    global $table_prefix,
           $wpdb;

    $table_name = $table_prefix . 'translations';

    $args = [
        'post_type' => [POST_TYPE, GLOSSARY_TYPE, EVENT_TYPE],
        'post_status' => 'publish',
        'posts_per_page' => -1,
        'lang' => DEFAULT_LANG,
    ];

    // get param from cli command
    $cliArgs = $_SERVER['argv'];
    if (!empty($cliArgs[1])) {
        $postIds = [$cliArgs[1]];
        if (!empty($postIds)) {
            $args['post__in'] = $postIds;
        }
    }

    $langs = get_available_languages();
    if (!empty($cliArgs[2])) {
        $langs = [$cliArgs[2]];
    }

    $posts = get_posts($args);
    foreach ($posts as $post) {
        $translationSkipped = false;
        /** @var WP_Post $post */
        $title = $post->post_title;
        $excerpt = $post->post_excerpt;
        $content = $post->post_content;

        $meta = get_post_meta($post->ID);
        if (!empty($meta)) {
            if (!empty($meta['_yoast_wpseo_title'])) {
                $metaTitle = $meta['_yoast_wpseo_title'][0];
            }
            if (!empty($meta['_yoast_wpseo_metadesc'])) {
                $metaDescription = $meta['_yoast_wpseo_metadesc'][0];
            }
        }

        $attributes = get_fields($post->ID);

        foreach ($langs as $lang) {
            $translatedAttributes = [];
            $translatedTitle = $translatedExcerpt = $translatedContent = $translatedMetaTitle = $translatedMetaDescription = '';
            $langParts = explode('_', $lang);
            $lang = $langParts[0];

            $postTranslation = getPostTranslation($post->ID, $lang, 1, true);
            $partialPostTranslation = getPostTranslation($post->ID, $lang, 0, true);

            if (!empty($partialPostTranslation[0])) {
                $translatedTitle = $partialPostTranslation[0]->post_title;
                $translatedExcerpt = $partialPostTranslation[0]->post_excerpt;
                $translatedContent = $partialPostTranslation[0]->post_content;
                if ($partialPostTranslation[0]->post_attributes) {
                    if (is_base64($partialPostTranslation[0]->post_attributes)) {
                        $partialPostTranslation[0]->post_attributes = base64_decode($partialPostTranslation[0]->post_attributes);
                    }
                    // Bad serialized strings will be rebuild on translation cron run
                    $translatedAttributes = @unserialize($partialPostTranslation[0]->post_attributes);

                    if (!is_array($translatedAttributes)) {
                        $message = 'Unserialize issue. Post #'.$partialPostTranslation[0]->post_id.' failed to unserialize.';
                        saveLog($message);
                        $translatedAttributes = [];
                    }
                }

                $translatedMetaTitle = $partialPostTranslation[0]->yoast_title;
                $translatedMetaDescription = $partialPostTranslation[0]->yoast_description;
            } elseif (!empty($postTranslation[0])) {
                $postTranslation = $postTranslation[0];
                if (strtotime($post->post_modified) < strtotime($postTranslation->date_modified)) {
                    // Skip post translation post was not modified
                    $translationSkipped = true;
                    $message = "Skip post #{$post->ID} to " . strtoupper($lang) . " as it was not modified since last translation.";
                    saveLog($message);
                    continue;
                }
            }

            $translatingAttributes = [];
            if (empty($translatedTitle) && !empty($title)) {
                $translatingAttributes[] = 'title';
            }
            if (empty($translatedExcerpt) && !empty($excerpt)) {
                $translatingAttributes[] = 'excerpt';
            }
            if (empty($translatedContent) && !empty($content)) {
                $translatingAttributes[] = 'content';
            }
            if (empty($translatedAttributes) && !empty($attributes)) {
                $translatingAttributes[] = 'attributes';
            }
            if (empty($translatedMetaTitle) && !empty($metaTitle) && !substr_count($metaTitle, '%')) {
                $translatingAttributes[] = 'meta title';
            }
            if (empty($translatedMetaDescription) && !empty($metaDescription)) {
                $translatingAttributes[] = 'meta description';
            }
            $additionalMessage = '';
            if (count($translatingAttributes) < 6) {
                $additionalMessage = "Translating only: " . implode(', ', $translatingAttributes);
            }
            saveLog("Start translating post #{$post->ID} to " . strtoupper($lang) . ". {$additionalMessage}");

            if (!empty($title) && empty($translatedTitle)) {
                $titleResponse = translateWithOpenAi($title, $lang, false);
                if (!empty($titleResponse['text'])) {
                    $translatedTitle = $titleResponse['text'];
                }
            }

            if (!empty($metaTitle) && !substr_count($metaTitle, '%') && empty($translatedMetaTitle)) {
                $metaTitleResponse = translateWithOpenAi($metaTitle, $lang, false);
                if (!empty($metaTitleResponse['text'])) {
                    $translatedMetaTitle = $metaTitleResponse['text'];
                }
            }

            if (!empty($metaDescription) && empty($translatedMetaDescription)) {
                $metaDescriptionResponse = translateWithOpenAi($metaDescription, $lang, false);
                if (!empty($metaDescriptionResponse['text'])) {
                    $translatedMetaDescription = $metaDescriptionResponse['text'];
                }
            }

            if ($excerpt && empty($translatedExcerpt)) {
                $excerptResponse = translateWithOpenAi($excerpt, $lang);
                if (!empty($excerptResponse['text'])) {
                    $translatedExcerpt = $excerptResponse['text'];
                }
            }

            if ($content && empty($translatedContent)) {
                $parts = preg_split('/\n\s*\n/', $content);
                foreach ($parts as $key => $part) {
                    if (preg_match('/(.*)\[video(.*)\](.*)/', trim($part))
                        || preg_match('/(.*)\[postLink(.*)\](.*)/', trim($part))) {
                        continue;
                    }
                    $part = do_shortcode($part);

                    $contentResponse = translateWithOpenAi($part, $lang);

                    if (!empty($contentResponse['text'])) {
                        $parts[$key] = $contentResponse['text'];
                    }
                    if ($key % 20 == 0) { // Delay to avoid rate limits
                        sleep(1);
                    }
                }
                $translatedContent = implode("\n\n", $parts);
            }

            if (!empty($attributes) && empty($translatedAttributes)) {
                foreach ($attributes as $akey => $value) {
                    if ($akey == 'word') {
                        // Not translating Glossary term
                        continue;
                    }
                    if (is_string($value) && !is_numeric($value) && !empty($value)) {
                        $attributeResponse = translateWithOpenAi($value, $lang);
                        if (!empty($attributeResponse['text'])) {
                            $translatedAttributes[$akey] = addslashes($attributeResponse['text']);
                        } else {
                            $message = "Error translating attribute: " . json_encode($value);
                            saveLog($message);
                        }

                    }
                }

                $translatedAttributes = $attributes;
                if (!empty($translatedAttributes)) {
                    $translatedAttributes = array_merge($attributes, $translatedAttributes);
                }

                if (!empty($attributes['faq'])) {
                    foreach ($attributes['faq'] as $key => $faq) {
                        $faqResponse = translateWithOpenAi($faq['question'], $lang);
                        if (!empty($faqResponse['text'])) {
                            $translatedAttributes['faq'][$key]['question'] = addslashes($faqResponse['text']);
                        } else {
                            saveLog("Error translating attribute: " . json_encode($value));
                        }
                        $faqResponse = translateWithOpenAi($faq['answer'], $lang);
                        if (!empty($faqResponse['text'])) {
                            $translatedAttributes['faq'][$key]['answer'] = addslashes($faqResponse['text']);
                        } else {
                            saveLog("Error translating attribute: " . json_encode($value));
                        }
                    }
                }

                if (!empty($attributes['youtube_video'])) {
                    foreach ($attributes['youtube_video'] as $key => $youtube_video) {
                        $youtubeVideoResponse = translateWithOpenAi($youtube_video['title'], $lang);
                        if (!empty($youtubeVideoResponse['text'])) {
                            $translatedAttributes['youtube_video'][$key]['title'] = addslashes($youtubeVideoResponse['text']);
                        } else {
                            saveLog("Error translating attribute: Video title ");
                        }
                        $youtubeVideoResponse = translateWithOpenAi($youtube_video['description'], $lang);
                        if (!empty($youtubeVideoResponse['text'])) {
                            $translatedAttributes['youtube_video'][$key]['description'] = addslashes($youtubeVideoResponse['text']);
                        } else {
                            saveLog("Error translating attribute: video description");
                        }

                        $translatedAttributes['youtube_video'][$key]['video-embed'] = $youtube_video['video-embed'];
                    }
                }
            }

            if (empty($translatedTitle)) {
                $translatedTitle = '';
            }
            if (empty($translatedExcerpt)) {
                $translatedExcerpt = '';
            }
            if (empty($translatedContent)) {
                $translatedContent = '';
            }
            if (empty($translatedAttributes)) {
                $translatedAttributes = '';
            }
            if (empty($translatedMetaTitle)) {
                $translatedMetaTitle = '';
            }
            if (empty($translatedMetaDescription)) {
                $translatedMetaDescription = '';
            }

            $translatedAttributes = base64_encode(serialize($translatedAttributes));

            $sql = "INSERT INTO $table_name 
                (post_id, lang, post_title, post_excerpt, post_content, post_attributes, yoast_title, yoast_description)
                 VALUES ($post->ID, '$lang', '" . addslashes($translatedTitle) . "', '" . addslashes($translatedExcerpt) . "', 
                '" . addslashes($translatedContent) . "', '" . $translatedAttributes . "',
                '" . addslashes($translatedMetaTitle) . "', '" . addslashes($translatedMetaDescription) . "') 
                   ON DUPLICATE KEY UPDATE 
                   post_title = VALUES(post_title), post_excerpt = VALUES(post_excerpt), 
                   post_content = VALUES(post_content), post_attributes = VALUES(post_attributes), 
                   yoast_title = VALUES(yoast_title),
                   yoast_description = VALUES(yoast_description),
                   is_active = 1,
                   date_modified = CURRENT_TIMESTAMP";
            $wpdb->query($sql);

            $message = "Post #{$post->ID} translated to " . strtoupper($lang);

            saveLog($message);
            sleep(5);
        }
    }
}

function translateWithOpenAi($text, $lang, $keepHtmlInResponse = true)
{
    if (preg_match('/^\[.*\]$/', $text)) {
        return $text;
    }

    if (trim(strip_tags($text)) == '') {
        return $text; // Return the text as is if it's html without text
    }

    $dotenv = Dotenv::createImmutable(ROOT);
    try {
        $dotenv->load();
    } catch (Exception $exception) {
        echo $exception->getMessage();
    }

    $settings = get_option('translation_settings');
    $token = $settings['gpt_token'] ?? '';

    $langs = [
        'es' => 'Spanish',
        'pt' => 'Portuguese',
        'ru' => 'Russian',
        'vi' => 'Vietnamese',
        'th' => 'Thai',
        'ko' => 'Korean',
    ];

    if (in_array($lang, array_keys($langs))) {
        $lang = $langs[$lang];
    } else {
        $lang = strtoupper($lang);
    }

    $prompt = 'Translate this text into ' . $lang . ' language. Keep HTML formating and special chars. If text contain HTML - return html. Else return just text. Dont add <html> tag. do not translate texts in [] (ex. [video id=""]. Dont use MARKDOWN. Text to translate: ' . $text;

    $headers = [
        'Authorization: Bearer ' . $token,
    ];

    $messages = [
        ['role' => 'user', 'content' => $prompt],
    ];

    $url = 'https://api.openai.com/v1/chat/completions';
    $data = [
        'model' => 'gpt-4o-mini-2024-07-18',
        'messages' => $messages,
        'temperature' => 0.7,
    ];

    $openAiResult = sendCurl($url, $data, $headers);

    if (!empty($openAiResult)) {
        $openAiResult = json_decode($openAiResult, true);
        if (!empty($openAiResult['choices'])) {
            $openAiResult = $openAiResult['choices'][0]['message']['content'];

            if (!$keepHtmlInResponse && function_exists('cleanHtmlTags')) {
                $openAiResult = cleanHtmlTags($openAiResult);
            }
        }
    }

    $response = [
        'text' => $openAiResult,
    ];

    return $response;
}

function translateWpUsers()
{
    global $table_prefix,
           $wpdb;

    $translation_table_name = $table_prefix . 'user_translations';

    $users = get_users();
    $langs = get_available_languages();

    foreach ($users as $user) {
        $userData = get_fields('user_' . $user->ID);//get_user_meta($user->ID);

        foreach ($langs as $lang) {
            try {
                $langParts = explode('_', $lang);
                $lang = strtolower($langParts[0]);

                $userTranslation = $wpdb->get_row(
                    $wpdb->prepare(
                        "SELECT * FROM $translation_table_name WHERE user_id = %d AND lang = %s",
                        $user->ID,
                        $lang
                    )
                );


                if (!empty($userTranslation) && $user->date_modified
                    && strtotime($userTranslation->date_modified) > strtotime($user->date_modified)) {
                    $message = "Skip user #{$user->ID} to " . strtoupper($lang) . " as it was not modified since last translation.";
                    saveLog($message);
                    continue; // Skip already translated users
                }

                $message = "Start translating user #{$user->ID} to " . strtoupper($lang);
                saveLog($message);

                $translatedDescription = '';

                if (!empty($user->description)) {
                    $descriptionResponse = translateWithOpenAi($user->description, $lang);
                    if (!empty($descriptionResponse['text'])) {
                        $translatedDescription = strip_tags($descriptionResponse['text']);
                    }
                }

                $translatedPosition = '';
                if (!empty($userData['position']) && !empty($userData['position'])) {
                    $positionResponse = translateWithOpenAi($userData['position'], $lang);
                    if (!empty($positionResponse['text'])) {
                        $translatedPosition = strip_tags($positionResponse['text']);
                    }
                }

                $translatedEducation = [];
                if (!empty($userData['education']) && !empty($userData['education'][0])) {
                    $translatedEducation = $userData['education'];
                    foreach ($translatedEducation as &$education) {
                        $educationResponse = translateWithOpenAi($education['title'], $lang);
                        if (!empty($educationResponse['text'])) {
                            $education['title'] = strip_tags($educationResponse['text']);
                        }
                        $educationResponse = translateWithOpenAi($education['subtitle'], $lang);
                        if (!empty($educationResponse['text'])) {
                            $education['subtitle'] = strip_tags($educationResponse['text']);
                        }
                    }
                }

                $translatedEducation = json_encode($translatedEducation, JSON_UNESCAPED_UNICODE);

                $translatedExperience = [];
                if (!empty($userData['experience']) && !empty($userData['experience'][0])) {
                    $translatedExperience = $userData['experience'];
                    foreach ($translatedExperience as &$experience) {
                        $experienceResponse = translateWithOpenAi($experience['position'], $lang);
                        if (!empty($experienceResponse['text'])) {
                            $experience['position'] = strip_tags($experienceResponse['text']);
                        }
                        $experienceResponse = translateWithOpenAi($experience['years'], $lang);
                        if (!empty($experienceResponse['text'])) {
                            $experience['years'] = strip_tags($experienceResponse['text']);
                        }
                        $experienceResponse = translateWithOpenAi($experience['description'], $lang);
                        if (!empty($experienceResponse['text'])) {
                            $experience['description'] = $experienceResponse['text'];
                        }
                    }
                }
                $translatedExperience = json_encode($translatedExperience, JSON_UNESCAPED_UNICODE);

                $now = date("Y-m-d H:i:s");
                $sql = "INSERT INTO $translation_table_name 
                (user_id, lang, description, position, education, experience, date_modified)
                 VALUES ($user->ID, '$lang', '" . addslashes($translatedDescription) . "', '" . addslashes($translatedPosition) . "', 
                    '" . $translatedEducation . "', '" . $translatedExperience . "', '{$now}')
                   ON DUPLICATE KEY UPDATE 
                   description = VALUES(description), 
                   position = VALUES(position),
                    education = VALUES(education),
                    experience = VALUES(experience),
                   date_modified = '{$now}'";

                $wpdb->query($sql);

                // For first translation
                if (!$user->date_modified) {
                    $translationTime = date('Y-m-d H:i:s');
                    $sql = "UPDATE $table_prefix" . "users SET date_modified = '$translationTime' WHERE ID = $user->ID";
                    $wpdb->query($sql);
                }

                $message = "User #{$user->ID} translated to " . strtoupper($lang);

                saveLog($message);
                sleep(1);
            } catch (\Exception $e) {
                $message = "Error translating user #{$user->ID} to " . strtoupper($lang) . ": " . $e->getMessage();
                saveLog($message);
            }
        }
    }
}

function translateWithApi($texts, $lang)
{
    if (!is_array($texts)) {
        $texts = [$texts];
    }

    if (count($texts) == 1 && preg_match('/^\[.*\]$/', $texts[0])) {
        return $texts; // Return the text as is if it's a single post link
    }

    $dotenv = Dotenv::createImmutable(ROOT);
    try {
        $dotenv->load();
    } catch (Exception $exception) {
        echo $exception->getMessage();
    }

    $initialUrl = "https://ftapi.pythonanywhere.com/translate?sl=en&dl={$lang}&text=";

    $response = [
        'text' => [],
    ];

    foreach ($texts as $text) {
        $text = str_replace(['&nbsp;', '\u00a0'], ' ', $text);
        if (empty(trim($text)) || in_array($text, [';', '0'])) {
            $response['text'][] = $text; // Keep texts as is
            continue;
        }

        $url = $initialUrl . urlencode($text);

        $translationResponse = sendCurl($url);

        $retryCount = 1;
        $translationResponse = json_decode($translationResponse, true);
        if (!empty($translationResponse['destination-text'])) {
            $response['text'][] = $translationResponse['destination-text'];
        } else {
            saveLog(json_encode($texts) . ' - ' . json_encode($translationResponse));

            while (is_null($translationResponse) || !isset($translationResponse['destination-text'])) {
                saveLog("Retrying " . $retryCount);
                sleep($retryCount * 10);
                $translationResponse = sendCurl($url);
                $translationResponse = json_decode($translationResponse, true);
                if (!empty($translationResponse['destination-text'])) {
                    $response['text'][] = $translationResponse['destination-text'];
                }
                if ($retryCount > 2) {
                    saveLog("Failed to translate after 3 retries: " . json_encode($texts));
                    $response['text'] = $texts; // Return original text if translation fails

                    return $response;
                }
                $retryCount++;
            }
        }

        sleep(1);
    }

    return $response;
}

function translateExpertise()
{
    global $wpdb, $table_prefix;

    $expertiseOptions = "SELECT term_id, name from {$table_prefix}terms WHERE term_id IN (SELECT term_id FROM {$table_prefix}term_taxonomy WHERE taxonomy = 'expertise')";

    $expertiseOptions = $wpdb->get_results($expertiseOptions);

    $langs = get_available_languages();
    foreach ($expertiseOptions as $expertiseOption) {
        foreach ($langs as $lang) {
            $lang = explode('_', $lang)[0];

            $query = $wpdb->prepare(
                "SELECT * FROM {$table_prefix}expertise_translations WHERE expertise_id = %d AND lang = %s",
                $expertiseOption->term_id,
                $lang
            );
            $translatedExpertise = $wpdb->get_row(
                $query
            );

            if (!empty($translatedExpertise)) {
                $message = "Skip {$expertiseOption->name} to " . strtoupper($lang) . " as it was already translated.";
                saveLog($message);
                continue; // Skip already translated expertise
            }

            $message = "Translating {$expertiseOption->name} to $lang";
            saveLog($message);

            $translatedExpertise = translateWithOpenAi($expertiseOption->name, $lang);

            if (!empty($translatedExpertise['text'])) {
                $translatedExpertise = strip_tags($translatedExpertise['text']);

                $wpdb->query(
                    $wpdb->prepare(
                        "INSERT INTO {$table_prefix}expertise_translations (expertise_id, lang, name) VALUES (%d, %s, %s)",
                        $expertiseOption->term_id,
                        $lang,
                        $translatedExpertise
                    )
                );
            }
        }
    }
}

function translateTags()
{
    global $wpdb, $table_prefix;

    $args = [
        'hide_empty' => false,
        'orderby' => 'name',
        'order' => 'ASC',
        'number' => 0,
        'format' => 'objects',
    ];

    $tags = get_terms([
        'taxonomy' => 'post_tag',
        'hide_empty' => (bool) $args['hide_empty'],
        'orderby' => $args['orderby'],
        'order' => $args['order'],
        'number' => (int) $args['number'],
    ]);

    if (is_wp_error($tags) || empty($tags)) {
        $tags = [];
    }

    $langs = get_available_languages();
    foreach ($tags as $tag) {
        foreach ($langs as $lang) {
            $lang = explode('_', $lang)[0];

            $query = $wpdb->prepare(
                "SELECT * FROM {$table_prefix}tag_translations WHERE tag_id = %d AND lang = %s",
                $tag->term_id,
                $lang
            );

            $translatedTag = $wpdb->get_row(
                $query
            );

            if (!empty($translatedTag)) {
                $message = "Skip tag {$tag->name} to " . strtoupper($lang) . " as it was already translated.";
                saveLog($message);
                continue; // Skip already translated tag
            }

            $message = "Translating tag {$tag->name} to $lang";
            saveLog($message);

            $translatedTag = translateWithOpenAi($tag->name, $lang);

            if (!empty($translatedTag['text']) && !is_array($translatedTag['text'])) {
                $translatedTag = strip_tags($translatedTag['text']);
                $wpdb->query(
                    $wpdb->prepare(
                        "INSERT INTO {$table_prefix}tag_translations (tag_id, lang, name) VALUES (%d, %s, %s)",
                        $tag->term_id,
                        $lang,
                        $translatedTag
                    )
                );
            }
        }
        sleep(1);
    }
}

$cliArgs = $_SERVER['argv'] ?? [];

if (empty($cliArgs[1])) {
    translateTags();
    translateExpertise();
    translateWpUsers();
}
translateWbPosts();

global $table_prefix,
       $wpdb;

$stats = $wpdb->get_row(
"SELECT count(*) as non_translated FROM {$table_prefix}translations WHERE is_active = 0 AND post_id IN (SELECT id FROM {$table_prefix}posts)"
);
saveLog('Non active translations: ' . $stats->non_translated);

delete_transient('doing_cron');
die();