Changeset 3170433
- Timestamp:
- 10/17/2024 03:46:28 AM (5 months ago)
- Location:
- fluentc-translation
- Files:
-
- 379 added
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
fluentc-translation/trunk/bootstrap.php
r3148988 r3170433 46 46 '\FluentC\Services\Widget', 47 47 '\FluentC\Services\Connect', 48 '\FluentC\Services\Html',49 48 '\FluentC\Services\Scan', 50 49 '\FluentC\Services\TranslationProcessor', 50 '\FluentC\Services\HtmlProcessor', 51 '\FluentC\Services\TranslationManager', 51 52 52 53 ); -
fluentc-translation/trunk/fluentc_autoload.php
r3122693 r3170433 14 14 * @return void 15 15 */ 16 function fluentc_autoload( $class_name) {17 $dir_class= __DIR__ . '/src/';18 $prefix = 'class-'; 19 $file_parts = explode( '\\', $class_name);16 function fluentc_autoload($class_name) { 17 $dir_class = __DIR__ . '/src/'; 18 $prefixes = ['class-', 'interface-', 'trait-']; // Add more prefixes if needed 19 $file_parts = explode('\\', $class_name); 20 20 21 $total_parts = count( $file_parts ) - 1; 22 $dir_file = $dir_class; 23 for ( $i = 1; $i <= $total_parts; $i++ ) { 24 if ( $total_parts !== $i ) { 25 $dir_file .= strtolower( $file_parts[ $i ] ) . '/'; 26 } else { 27 $string = str_replace( '_', '-', strtolower( $file_parts[ $i ] ) ); 28 $file_load = $dir_file . $prefix . $string . '.php'; 21 $total_parts = count($file_parts) - 1; 22 $dir_file = $dir_class; 23 for ($i = 1; $i <= $total_parts; $i++) { 24 if ($total_parts !== $i) { 25 $dir_file .= strtolower($file_parts[$i]) . '/'; 26 } else { 27 $string = str_replace('_', '-', strtolower($file_parts[$i])); 28 29 // Try each prefix 30 foreach ($prefixes as $prefix) { 31 $file_load = $dir_file . $prefix . $string . '.php'; 32 if (file_exists($file_load)) { 33 include_once $file_load; 34 return; 35 } 36 } 29 37 30 if ( file_exists( $file_load ) ) { 31 include_once $file_load; 32 } 33 } 34 } 38 // If no prefixed file found, try without prefix 39 $file_load = $dir_file . $string . '.php'; 40 if (file_exists($file_load)) { 41 include_once $file_load; 42 return; 43 } 44 } 45 } 35 46 } -
fluentc-translation/trunk/fluentc_wordpress_plugin.php
r3162981 r3170433 5 5 /** 6 6 * Plugin Name: FluentC Translation 7 * Plugin URI: https:// github.com/fluentc/wordpress-plugin7 * Plugin URI: https://www.fluentc.ai 8 8 * Description: A plugin that enables website owners to easily install the FluentC Translation on their WordPress site. 9 * Version: 1.9.69 * Version: 2.0 10 10 * Author: FluentC 11 11 * Author URI: https://www.fluentc.ai … … 17 17 define( 'FLUENTC_DIR', __DIR__ ); 18 18 define( 'FLUENTC_SLUG', 'fluentc_translation' ); 19 define( 'FLUENTC_TRANSLATION_VERSION', " 1.9.6" );19 define( 'FLUENTC_TRANSLATION_VERSION', "2.0" ); 20 20 define( 'FLUENTC_TRANSLATION_PLUGIN_DIR', plugin_dir_path(__FILE__) ); 21 21 define( 'FLUENTC_TRANSLATION_PLUGIN_URL', plugin_dir_url(__FILE__) ); -
fluentc-translation/trunk/readme.txt
r3162981 r3170433 5 5 Requires at least: 4.6 6 6 Tested up to: 6.6.2 7 Stable tag: 1.9.67 Stable tag: 2.0 8 8 Requires PHP: 7.3 9 9 License: GPLv2 or later -
fluentc-translation/trunk/src/actions/class-wordpress.php
r3162981 r3170433 17 17 use FluentC\Models\Htmltags; 18 18 use FluentC\Services\Connect; 19 use FluentC\Services\Html;20 19 use FluentC\Utils\Language; 21 20 use FluentC\Services\Cache; 22 use PHPHtmlParser\Dom; 23 use PHPHtmlParser\Options; 21 use FluentC\Services\Html_Processor; 22 use FluentC\Services\Translation_Manager; 23 use FluentC\Utils\Regex_Helper; 24 use FluentC\Utils\Placeholder_Manager; 25 use FluentC\Utils\Performance_Monitor; 24 26 25 27 /** … … 90 92 */ 91 93 protected $widgetapikey; 92 93 94 95 /** 96 * HtmlProcessor instance 97 * 98 * @var Html_Processor 99 */ 100 protected $htmlProcessor; 101 102 private $translation_manager; 103 private $regexHelper; 104 private $placeholderManager; 94 105 /** 95 106 * Constructor. … … 97 108 * @since 1.2 98 109 */ 99 public function __construct() { 100 $this->fluentc_connect = new Connect(); 101 $this->fluentc_language = new Language(); 102 $this->fluentc_htmltags = new Htmltags(); 103 $this->fluentc_cache = new Cache(); 104 $this->translated_text = array(); 105 $this->language_code = $this->fluentc_language->get_fluentc_language(); 106 $this->widgetapikey = get_option( 'fluentc_api_key' ); 107 $this->site_language = $this->fluentc_language->fluentc_site_language(); 108 $this->fluentc_html = new Html($this->site_language); 109 110 } 110 public function __construct() { 111 $this->fluentc_connect = new Connect(); 112 $this->fluentc_language = new Language(); 113 $this->fluentc_htmltags = new Htmltags(); 114 $this->fluentc_cache = new Cache(); 115 $this->translated_text = array(); 116 $this->language_code = $this->fluentc_language->get_fluentc_language(); 117 $this->widgetapikey = get_option( 'fluentc_api_key' ); 118 $this->site_language = $this->fluentc_language->fluentc_site_language(); 119 // Initialize new components 120 121 122 } 111 123 112 124 /** … … 116 128 */ 117 129 public function hooks() { 118 add_filter( 'the_title', array( $this, 'fluentcFilterTitle' ), 9999, 2 );130 /* add_filter( 'the_title', array( $this, 'fluentcFilterTitle' ), 9999, 2 ); 119 131 add_filter( 'the_content', array( $this, 'filter_content' ), 9999, 1 ); 120 132 add_filter( 'widget_text', array( $this, 'fluentcFilterWidgetText' ), 9999, 2 ); … … 128 140 add_filter( 'woocommerce_product_shortcode_content', array( $this, 'filter_content' ), 100, 1 ); 129 141 add_filter( 'woocommerce_short_description', array( $this, 'filter_content' ), 100, 1 ); 130 add_filter( 'woocommerce_ cart_shortcode_content', array( $this, 'filter_content' ), 100, 1 );142 add_filter( 'woocommerce_get_script_data', array( $this, 'ffluentc_woo_script_data' ), 100, 1 ); 131 143 add_filter( 'shutdown', array( $this, 'fluentc_shutdown' ), 100, 1 ); 132 add_filter( 'init', array( $this, 'fluentc_language_redirect' ), 10 ); 133 add_filter( 'woocommerce_get_script_data', array( $this, 'fluentc_woo_script_data' ), 10, 2 ); 144 */ 145 add_filter( 'woocommerce_ajax_get_endpoint', array( $this, 'correct_multilingual_ajax_endpoint' ), 100, 2 ); 146 add_action('init', [$this, 'init_output_buffering'], 0); 147 add_action('shutdown', [$this, 'fluentc_flush_output_buffer'], 0); 134 148 } 135 149 136 150 137 /** 138 * Modify WooCommerce script data. 139 * 140 * This function intercepts the data being localized for WooCommerce scripts 141 * and modifies the AJAX URL for the cart fragments script. This is useful 142 * for multilingual setups where the default AJAX URL might not work correctly. 143 * 144 * @param array $params The original script parameters. 145 * @param string $handle The script handle. 146 * @return array The modified script parameters. 147 */ 148 149 public function fluentc_woo_script_data( $params, $handle ) { 150 if ( 'wc-cart-fragments' === $handle ) { 151 $params['ajax_url'] = get_bloginfo('url') . '/?wc-ajax=%%endpoint%%'; 152 } 153 return $params; 154 } 155 /** 156 * Filters Blocks 157 * 158 * @param string $block_content Content of a Block. 159 * @param string $block Block Object. 160 */ 161 public function filter_block($block_content, $block) 151 public function correct_multilingual_ajax_endpoint($endpoint, $request) { 152 // Get the site's home URL 153 $home_url = home_url('/'); 154 155 // Parse the current URL 156 $parsed_url = parse_url($endpoint); 157 158 // If the host is missing or incorrect, rebuild the URL 159 if (!isset($parsed_url['host']) || $parsed_url['host'] !== parse_url($home_url, PHP_URL_HOST)) { 160 $corrected_endpoint = $home_url; 161 162 // Remove the leading slash if it exists 163 $path = ltrim($parsed_url['path'], '/'); 164 165 // Reconstruct the URL 166 $corrected_endpoint .= $path; 167 if (isset($parsed_url['query'])) { 168 $corrected_endpoint .= '?' . $parsed_url['query']; 169 } 170 171 return $corrected_endpoint; 172 } 173 174 return $endpoint; 175 } 176 177 public function init_output_buffering() 162 178 { 163 164 if (!$this->should_process_content($block_content)) { 165 return $block_content; 166 } 167 168 $dom = $this->load_dom($block_content); 169 170 $texts_to_translate = $entry_map = $entry_skip_map = []; 171 172 $this->process_dom_elements($dom, $texts_to_translate, $entry_map, $entry_skip_map); 173 174 if (!empty($texts_to_translate)) { 175 $this->process_translations($texts_to_translate, $entry_map, $entry_skip_map); 176 } 177 178 $html = $this->get_processed_html($dom); 179 180 return $html ?: $block_content; 181 } 182 183 /** 184 * Apply translations to nodes 185 */ 186 private function applyTranslations($entry_map, $translated_texts, $language_code) 187 { 188 foreach ($translated_texts as $translatedText) { 189 $original_text = $translatedText->originalText; 190 $translated_text = $translatedText->translatedText; 191 192 if (isset($entry_map[$original_text]) && $translated_text !== null && $translated_text !== '') { 193 foreach ($entry_map[$original_text] as $node) { 194 $this->applyTranslationToNode($node, $translated_text, $node->getDataAttributes()); 195 } 196 197 $this->update_translation_cache($original_text, $translated_text, $language_code); 198 } else { 199 do_action('qm/info', 'No Translated Text or No Matching Nodes'); 200 } 201 } 202 } 203 179 ob_start([$this, 'process_final_output']); 180 } 181 182 public function process_final_output($buffer) 183 { 184 // Don't process admin pages. 185 if (is_admin()) { 186 return $buffer; 187 } 188 // Don't process RSS feeds. 189 if (is_feed()) { 190 return $buffer; 191 } 192 193 // Don't process JSON responses. 194 if ($this->is_json($buffer)) { 195 return $buffer; 196 } 197 198 // Process the entire page content. 199 return $this->filter_content($buffer); 200 } 201 202 private function is_json($string) { 203 json_decode($string); 204 return (json_last_error() == JSON_ERROR_NONE); 205 } 206 public function fluentc_flush_output_buffer() 207 { 208 if (ob_get_length()) { 209 ob_end_flush(); 210 } 211 } 204 212 /** 205 * Apply skipped translations 206 */ 207 private function applySkippedTranslations($entry_skip_map) 208 { 209 foreach ($entry_skip_map as $node) { 210 $original_text = $node->text(); 211 $key = hash('md5', $original_text); 212 213 if (isset($this->translated_text[$key]) && null !== $this->translated_text[$key]) { 214 $this->applyTranslationToNode($node, $this->translated_text[$key], $node->getDataAttributes()); 215 } 216 } 217 } 218 219 /** 220 * Process individual node 221 */ 222 private function processNode($node, &$texts_to_translate, &$entry_map, &$entry_skip_map) 223 { 224 if ($node instanceof \PHPHtmlParser\Dom\Node\HtmlNode) { 225 // Process child nodes of HTML elements 226 foreach ($node->getChildren() as $child) { 227 $this->processNode($child, $texts_to_translate, $entry_map, $entry_skip_map); 228 } 229 return; 230 } 231 232 if (!($node instanceof \PHPHtmlParser\Dom\Node\TextNode)) { 233 return; 234 } 235 236 if ($this->should_skip_node($node)) { 237 return; 238 } 239 240 $text = $node->text(); 241 242 if ($this->is_shortcode($text)) { 243 return; 244 } 245 246 if ($this->is_empty_or_numeric($text)) { 247 return; 248 } 249 250 if (in_array($text, $this->fluentc_htmltags->forbidden_selectors)) { 251 $node->setText(''); 252 return; 253 } 254 255 $this->process_node_text($node, $text, $texts_to_translate, $entry_map, $entry_skip_map); 256 } 257 258 /** 259 * Apply translation to node 260 */ 261 private function applyTranslationToNode($node, $translatedText, $dataAttributes) 262 { 263 $node->setText($translatedText); 264 foreach ($dataAttributes as $dataKey => $dataValue) { 265 $node->tag->setAttribute($dataKey, $dataValue); 266 } 267 } 268 /** 269 * Filters Content 270 * 271 * @param string $content Content Object. 272 */ 273 public function filter_content( $content ) { 274 213 * Filters Content 214 * 215 * @param string $content Content Object. 216 */ 217 public function filter_content($content) 218 { 275 219 if (!$this->should_process_content($content)) { 276 220 return $content; 277 221 } 278 279 $dom = $this->load_dom($content); 280 $texts_to_translate = $entry_map = $entry_skip_map = []; 281 282 $this->process_dom_elements($dom, $texts_to_translate, $entry_map, $entry_skip_map); 283 284 if (!empty($texts_to_translate)) { 285 $this->process_translations($texts_to_translate, $entry_map, $entry_skip_map); 286 } 287 288 $html = $this->get_processed_html($dom); 289 290 return $html ?: $content; 291 } 292 293 /** 294 * Updates Home page link 295 * 296 * @param string $title Title of the posts. 297 * @param string $id id of the post. 298 */ 222 $translation_manager = $this->get_translation_manager(); 223 if ($translation_manager === null) { 224 return $content; // No translation needed 225 } 226 227 $html = $this->htmlProcessor->processHtml( 228 $content, 229 $this->site_language, 230 $this->language_code 231 ); 232 233 return $html ?: $content; 234 } 235 236 237 /** 238 * Helper function to check if content should be processed 239 */ 240 private function should_process_content($content) 241 { 242 return $this->language_code && !is_null($content) && $content !== ''; 243 } 244 245 246 299 247 /** 248 * Filters Blocks 249 * 250 * @param string $block_content Content of a Block. 251 * @param string $block Block Object. 252 */ 253 public function filter_block($block_content, $block) 254 { 255 if (!$this->should_process_content($block_content)) { 256 return $block_content; 257 } 258 259 $translation_manager = $this->get_translation_manager(); 260 if ($translation_manager === null) { 261 return $block_content; // No translation needed 262 } 263 264 $html = $this->htmlProcessor->processHtml( 265 $block_content, 266 $this->site_language, 267 $this->language_code, 268 ); 269 270 // Remove the translated flag before returning 271 return $html ?: $block_content; 272 } 273 /** 300 274 * Filter title 301 275 */ 302 276 public function fluentcFilterTitle($title, $id = null) 303 277 { 304 305 278 if (!$this->language_code) { 306 279 return $title; 307 280 } 308 309 $key = hash('md5', $title . ($id ? "_$id" : '')); 310 311 if (isset($this->translated_text[$key])) { 312 return $this->translated_text[$key]; 313 } 314 315 $translated_text = $this->get_cached_translation($key); 316 if ($translated_text) { 317 return $translated_text; 318 } 319 320 $translated_data = $this->fluentc_connect->get_translation_text( 321 $this->widgetapikey, 322 $this->site_language, 323 $this->language_code, 324 $title, 325 $id 281 $translation_manager = $this->get_translation_manager(); 282 if ($translation_manager === null) { 283 return $title; // No translation needed 284 } 285 286 $html = $this->htmlProcessor->processHtml( 287 $title, 288 $this->site_language, 289 $this->language_code 290 291 ); 292 293 return $html ?: $title; 294 } 295 296 297 /** 298 * Updates checkout link 299 * 300 * @param mixed $output HTML Content. 301 * @param mixed $tag HTML Content. 302 * @param mixed $attr HTML Content. 303 */ 304 public function fluentc_woocommerce_checkout_shortcode_filter($output, $tag, $attr) 305 { 306 if (!$this->should_process_content($output)) { 307 return $output; 308 } 309 $translation_manager = $this->get_translation_manager(); 310 if ($translation_manager === null) { 311 return $output; // No translation needed 312 } 313 $html = $this->htmlProcessor->processHtml( 314 $output, 315 $this->site_language, 316 $this->language_code 317 326 318 ); 327 328 if (isset($translated_data->data->translateSite->body)) { 329 $translated_text = $translated_data->data->translateSite->body[0]->translatedText; 330 $this->update_translation_cache($title, $translated_text, $this->language_code); 331 return $translated_text; 332 } else { 333 $this->translated_text[$key] = $title; 334 do_action('qm/error', 'Translation failed for title: ' . $title); 335 return $title; 336 } 337 } 338 319 320 return $html ?: $output; 321 } 322 323 324 /** 325 * Updates Home page link 326 * 327 * @param string $text Text Content. 328 * @param string $instance Instance details of the widget. 329 */ 330 public function fluentcFilterWidgetText($text, $instance) 331 { 332 if (!$this->should_process_content($text)) { 333 return $text; 334 } 335 336 $html = $this->htmlProcessor->processHtml( 337 $text, 338 $this->site_language, 339 $this->language_code 340 ); 341 342 return $html ?: $text; 343 } 339 344 340 341 /**342 * Updates Home page link343 *344 * @param string $text Text Content.345 * @param string $instance Instance details of the widget.346 */347 public function fluentcFilterWidgetText( $text, $instance ) {348 {349 350 if (!$this->should_process_content($text)) {351 return $text;352 }353 354 $dom = $this->load_dom($text);355 $texts_to_translate = $entry_map = $entry_skip_map = [];356 357 $this->process_dom_elements($dom, $texts_to_translate, $entry_map, $entry_skip_map);358 359 if (!empty($texts_to_translate)) {360 $this->process_translations($texts_to_translate, $entry_map, $entry_skip_map);361 }362 363 $html = $this->get_processed_html($dom);364 365 return $html ?: $text;366 }367 }368 369 /**370 * Updates checkout link371 *372 * @param mixed $output HTML Content.373 * @param mixed $tag HTML Content.374 * @param mixed $attr HTML Content.375 */376 public function fluentc_woocommerce_checkout_shortcode_filter( $output, $tag, $attr ){377 {378 379 if (!$this->should_process_content($output)) {380 return $output;381 }382 383 $dom = $this->load_dom($output);384 $texts_to_translate = $entry_map = $entry_skip_map = [];385 386 $this->process_dom_elements($dom, $texts_to_translate, $entry_map, $entry_skip_map);387 388 if (!empty($texts_to_translate)) {389 $this->process_translations($texts_to_translate, $entry_map, $entry_skip_map);390 }391 392 $html = $this->get_processed_html($dom);393 394 return $html ?: $output;395 }396 }397 345 398 346 /** … … 406 354 } 407 355 408 /** 409 * Process text labels by removing duplicates and formatting for use in a query. 410 * 411 * @param array $texts Array of text labels to process. 412 * @return string Formatted string of unique text labels. 413 */ 414 public function process_text_labels($texts) 415 { 416 $unique_texts = array_unique(array_filter($texts)); 417 return implode(', ', array_map(function($text) { 418 return '"' . str_replace('"', '"', $text) . '"'; 419 }, $unique_texts)); 420 } 421 422 423 424 /** 425 * Helper function to check if content should be processed 426 */ 427 private function should_process_content($content) 428 { 429 return $this->language_code && !is_null($content) && $content !== ''; 430 } 431 432 /** 433 * Helper function to load DOM 434 */ 435 private function load_dom($content) 436 { 437 $html = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 438 $options = new Options(); 439 $options->setCleanupInput(false); 440 $options->setWhitespaceTextNode(false); 441 $options->setRemoveStyles(false); 442 $options->setPreserveLineBreaks(true); 443 $dom = new Dom; 444 $dom->loadStr($html, $options); 445 return $dom; 446 } 447 448 /** 449 * Helper function to process DOM elements 450 */ 451 private function process_dom_elements($dom, &$texts_to_translate, &$entry_map, &$entry_skip_map) 452 { 453 $elements = $dom->find('*:not(script):not(style):not(doctype):not(code):not(figure):not(pre):not(noscript):not(iframe):not(object):not(embed):not(svg):not(math):not(canvas)'); 454 foreach ($elements as $element) { 455 $nodes = ($element instanceof \PHPHtmlParser\Dom\Node\HtmlNode) ? $element->getChildren() : [$element]; 456 foreach ($nodes as $node) { 457 $this->processNode($node, $texts_to_translate, $entry_map, $entry_skip_map); 458 } 459 } 460 } 461 462 /** 463 * Helper function to process translations 464 */ 465 private function process_translations(&$texts_to_translate, &$entry_map, &$entry_skip_map) 466 { 467 $uncached_texts = []; 468 $cached_translations = []; 469 470 foreach ($texts_to_translate as $text) { 471 $cached_translation = $this->get_cached_translation(hash('md5', $text)); 472 if ($cached_translation) { 473 $cached_translations[] = $cached_translation; 474 } else { 475 $uncached_texts[] = $text; 476 } 477 } 478 479 if (!empty($uncached_texts)) { 480 $text_labels = $this->process_text_labels($uncached_texts); 481 $translated_texts = $this->fluentc_connect->get_translation_content( 482 $this->widgetapikey, 483 $this->site_language, 484 $this->language_code, 485 $text_labels 486 ); 487 488 if (isset($translated_texts->data->translateSite->body) && !empty($translated_texts->data->translateSite->body)) { 489 $cached_translations = array_merge($cached_translations, $translated_texts->data->translateSite->body); 490 } else { 491 do_action('qm/debug', 'Translation error: Data not found in response.'); 492 } 493 } 494 495 $this->applyTranslations($entry_map, $cached_translations, $this->language_code); 496 } 497 498 /** 499 * Helper function to get processed HTML 500 */ 501 private function get_processed_html($dom) 502 { 503 if ($dom->root && method_exists($dom->root, 'outerHtml')) { 504 try { 505 $html = $dom->root->outerHtml(); 356 357 private function get_translation_manager() 358 { 359 if ($this->translation_manager === null) { 506 360 507 // Remove the root tag if it exists 508 $html = preg_replace('/<root>|<\/root>/', '', $html); 361 $this->translation_manager = new Translation_Manager( 362 $this->fluentc_connect, 363 $this->fluentc_cache, 364 $this->site_language, 365 $this->language_code 366 ); 367 368 $this->regexHelper = new Regex_Helper(); 369 $this->placeholderManager = new Placeholder_Manager(); 370 $performanceMonitor = new Performance_Monitor() ; 371 $this->htmlProcessor = new Html_Processor($this->regexHelper, $this->placeholderManager ,$this->translation_manager, $this->fluentc_htmltags); 509 372 510 if ($html !== null && $html !== '' && is_string($html) && strlen(trim($html)) > 0) { 511 // Temporarily replace complex structures. 512 $html = $this->replace_complex_structures($html); 513 514 // Process only text nodes for translation. 515 $html = preg_replace_callback('/>((?:[^<]|<(?!\/?\w+(?:\s|>)))+)</s', function($matches) { 516 $text = $matches[1]; 517 if (trim($text) !== '') { 518 $translated_text = $this->translate_text($text); 519 return '>' . $translated_text . '<'; 520 } 521 return $matches[0]; 522 }, $html); 523 524 // Restore complex structures 525 $html = $this->restore_complex_structures($html); 526 527 return $html; 528 } 529 } catch (\Exception $e) { 530 do_action('qm/error', 'Error in get_processed_html: ' . $e->getMessage()); 531 } 532 } else { 533 do_action('qm/info', 'Root or outerHtml method not found'); 534 } 535 return null; 536 } 537 538 /** 539 * Summary of replace_complex_structures 540 * @param mixed $html 541 * @return array|string|null 542 */ 543 private function replace_complex_structures($html) 544 { 545 // Replace iframes 546 $html = preg_replace_callback('/<iframe\b[^>]*>(.*?)<\/iframe>/is', function($matches) { 547 return '###IFRAME' . base64_encode($matches[0]) . '###'; 548 }, $html); 549 550 // Replace data attributes 551 $html = preg_replace_callback('/\s(data-[^\s"\']+(?:=(?:"[^"]*"|\'[^\']*\'|[^\s"\']+))?)/i', function($matches) { 552 return ' ###DATA' . base64_encode($matches[0]) . '###'; 553 }, $html); 554 555 // Replace complex class attributes 556 $html = preg_replace_callback('/\sclass="[^"]+"/i', function($matches) { 557 return ' ###CLASS' . base64_encode($matches[0]) . '###'; 558 }, $html); 559 560 // Preserve entire Elementor video widget 561 $html = preg_replace_callback('/<div class="elementor-element[^>]*elementor-widget-video[^>]*>.*?<\/div>/s', function($matches) { 562 return '###ELEMENTOR_VIDEO_WIDGET' . base64_encode($matches[0]) . '###'; 563 }, $html); 564 // Preserve Elementor video widget data-settings 565 $html = preg_replace_callback('/(data-settings=)(["\'])(.+?)\2/s', function($matches) { 566 return $matches[1] . $matches[2] . '###ELEMENTOR_VIDEO_SETTINGS' . base64_encode($matches[3]) . '###' . $matches[2]; 567 }, $html); 568 569 return $html; 570 } 571 572 /** 573 * Summary of restore_complex_structures 574 * @param mixed $html 575 * @return array|string|null 576 */ 577 private function restore_complex_structures($html) 578 { 579 // Restore iframes 580 $html = preg_replace_callback('/###IFRAME(.*?)###/', function($matches) { 581 return base64_decode($matches[1]); 582 }, $html); 583 584 // Restore data attributes 585 $html = preg_replace_callback('/###DATA(.*?)###/', function($matches) { 586 return ' ' . base64_decode($matches[1]); 587 }, $html); 588 589 // Restore complex class attributes 590 $html = preg_replace_callback('/###CLASS(.*?)###/', function($matches) { 591 return base64_decode($matches[1]); 592 }, $html); 593 594 // Restore entire Elementor video widget 595 $html = preg_replace_callback('/###ELEMENTOR_VIDEO_WIDGET(.+?)###/', function($matches) { 596 return base64_decode($matches[1]); 597 }, $html); 598 599 // Restore Elementor video widget data-settings 600 $html = preg_replace_callback('/###ELEMENTOR_VIDEO_SETTINGS(.+?)###/', function($matches) { 601 return base64_decode($matches[1]); 602 }, $html); 603 604 return $html; 605 } 606 607 /** 608 * Summary of translate_text 609 * @param mixed $text 610 * @return mixed 611 */ 612 private function translate_text($text) 613 { 614 $key = hash('md5', $text); 615 if (isset($this->translated_text[$key])) { 616 return $this->translated_text[$key]; 617 } 618 619 $cached_translation = $this->get_cached_translation($key); 620 if ($cached_translation) { 621 return $cached_translation; 622 } 623 624 // Return the original text. 625 return $text; 626 } 627 628 /** 629 * Summary of is_shortcode 630 * @param mixed $text 631 * @return bool 632 */ 633 private function is_shortcode($text) { 634 return preg_match('/^\[[\w\-]+(?:\s+[\w\-]+="[^"]*")*\s*\]$/', trim($text)) === 1; 635 } 636 /** 637 * Helper function to check if node should be skipped 638 */ 639 private function should_skip_node($node) 640 { 641 $parent = $node->getParent(); 642 while ($parent !== null) { 643 if ($parent instanceof \PHPHtmlParser\Dom\Node\HtmlNode) { 644 $tag_name = strtolower($parent->tag->name()); 645 if (in_array($tag_name, ['style', 'svg'])) { 646 return true; 647 } 648 } 649 $parent = $parent->getParent(); 650 } 651 return false; 652 } 653 654 /** 655 * Helper function to check if text is empty or numeric 656 */ 657 private function is_empty_or_numeric($text) 658 { 659 return preg_match($this->fluentc_htmltags->regex_only_whitespace, $text) || 660 preg_match($this->fluentc_htmltags->regex_only_digits_whitespace_punctuation, $text); 661 } 662 663 /** 664 * Helper function to process node text 665 */ 666 private function process_node_text($node, $text, &$texts_to_translate, &$entry_map, &$entry_skip_map) 667 { 668 $key = hash('md5', $text); 669 $dataAttributes = $node->getDataAttributes(); 670 671 if (array_key_exists($key, $this->translated_text)) { 672 $this->applyTranslationToNode($node, $this->translated_text[$key], $dataAttributes); 673 $entry_skip_map[] = $node; 674 return; 675 } 676 677 $cached_translation = $this->get_cached_translation($key); 678 if ($cached_translation) { 679 $this->apply_cached_translation($node, $cached_translation, $dataAttributes, $key, $entry_skip_map); 680 return; 681 } 682 683 if (!in_array($text, $texts_to_translate)) { 684 $texts_to_translate[] = $text; 685 } 686 687 if (!isset($entry_map[$text])) { 688 $entry_map[$text] = []; 689 } 690 $entry_map[$text][] = $node; 691 } 692 693 /** 694 * Helper function to get cached translation 695 */ 696 private function get_cached_translation($key) 697 { 698 $cache_key = $this->site_language . $this->language_code . $key; 699 $cached_translation = $this->fluentc_cache->get($cache_key); 700 if ($cached_translation) { 701 $json_cache = json_decode($cached_translation); 702 if (isset($json_cache->data->translateSite->body->translatedText)) { 703 return $json_cache->data->translateSite->body->translatedText; 704 } 705 } 706 return null; 707 } 708 709 /** 710 * Helper function to apply cached translation 711 */ 712 private function apply_cached_translation($node, $translated_text, $dataAttributes, $key, &$entry_skip_map) 713 { 714 $node->setText($translated_text); 715 foreach ($dataAttributes as $dataKey => $dataValue) { 716 $node->tag->setAttribute($dataKey, $dataValue); 717 } 718 $translated_key = hash('md5', $translated_text); 719 $this->translated_text[$key] = $translated_text; 720 $this->translated_text[$translated_key] = $translated_text; 721 $entry_skip_map[] = $node; 722 } 723 724 /** 725 * Helper function to update translation cache 726 */ 727 private function update_translation_cache($original_text, $translated_text, $language_code) 728 { 729 $key = hash('md5', $original_text); 730 $translated_key = hash('md5', $translated_text); 731 $this->translated_text[$key] = $translated_text; 732 $this->translated_text[$translated_key] = $translated_text; 733 $cache_key = $this->site_language . $language_code . $key; 734 $this->fluentc_cache->set($cache_key, json_encode([ 735 'data' => ['translateSite' => ['body' => [ 736 'sourceLanguage' => $this->site_language, 737 'targetLanguage' => $language_code, 738 'translatedText' => $translated_text, 739 'originalText' => $original_text 740 ]]] 741 ])); 742 } 743 373 } 374 return $this->translation_manager; 375 } 744 376 745 377 /** -
fluentc-translation/trunk/src/fluentc_pll_api.php
r3159410 r3170433 104 104 */ 105 105 function pll_languages_list( $args = array() ) { 106 $fluentc_connect = new Connect(); 107 $args = wp_parse_args( $args, array( 'fields' => 'slug' ) ); 108 109 $result = $fluentc_connect->get_fluentc_languages_list( $args ); 110 return is_array($result) ? $result : array(); 111 } 112 106 $fluentc_connect = new Connect(); 107 108 // Always set 'fields' to 'slug' to ensure we get an array of slugs 109 $args['fields'] = 'slug'; 110 111 $result = $fluentc_connect->get_pll_fluentc_languages_list( $args ); 112 113 // Ensure we always return an array of strings (slugs) 114 if ( !is_array($result) || empty($result) ) { 115 return array(); 116 } 117 118 // If any elements are still objects, extract the slug 119 return array_map(function($item) { 120 return is_object($item) ? $item->slug : $item; 121 }, $result); 122 } 113 123 114 124 /** -
fluentc-translation/trunk/src/models/class-htmltags.php
r3159410 r3170433 36 36 '._fluentc_widget-language-list', 37 37 '._fluentc_widget_float_dropdown_wrapper', 38 'wpadminbar' 38 39 ); 39 40 … … 176 177 } 177 178 179 public function processLink(string $url, string $language_code): string { 180 $parsed_url = parse_url($url); 181 $is_relative_path = empty($parsed_url['host']); 182 183 // Check if the URL should be processed 184 if ($this->shouldSkipProcessing($url, $parsed_url, $is_relative_path)) { 185 return $url; 186 } 187 188 // Check if the language code is already present 189 if ($this->hasLanguageCode($parsed_url['path'], $language_code)) { 190 return $url; 191 } 192 193 return $this->addLanguageCodeToLink($language_code, $url); 194 } 195 196 private function shouldSkipProcessing($url, $parsed_url, $is_relative_path): bool { 197 return $is_relative_path || 198 (isset($parsed_url['host']) && parse_url(home_url(), PHP_URL_HOST) !== $parsed_url['host']) || 199 strpos($url, '/wp-admin/') !== false || 200 $this->check_for_file($url); 201 } 202 203 private function hasLanguageCode($path, $language_code): bool { 204 $path_segments = explode('/', trim($path, '/')); 205 return !empty($path_segments) && $path_segments[0] === $language_code; 206 } 207 208 private function addLanguageCodeToLink($language_code, $url) { 209 $url_parts = wp_parse_url($url); 210 211 $original_path = isset($url_parts['path']) ? $url_parts['path'] : ''; 212 $path = '/' . $language_code . $original_path; 213 214 $new_permalink = ''; 215 216 if (isset($url_parts['scheme']) && isset($url_parts['host'])) { 217 $new_permalink = $url_parts['scheme'] . '://' . $url_parts['host']; 218 } 219 220 $new_permalink .= $path; 221 222 if (!empty($url_parts['query'])) { 223 $new_permalink .= '?' . $url_parts['query']; 224 } 225 226 if (!empty($url_parts['fragment'])) { 227 $new_permalink .= '#' . $url_parts['fragment']; 228 } 229 230 return $new_permalink; 231 } 178 232 /** 179 233 * Updates Links … … 215 269 return $new_permalink; 216 270 } 271 272 /*public function processLink(string $url, string $language_code): string 273 { 274 //$current_language_code = $this->getLanguageFromUrl($url, $regex_lang); 275 $parsed_url = parse_url($url); 276 $is_relative_path = empty($parsed_url['host']); 277 $root_url = home_url(); 278 279 if ( 280 $is_relative_path || 281 (isset($parsed_url['host']) && parse_url($root_url, PHP_URL_HOST) !== $parsed_url['host']) || 282 strpos($url, '/wp-admin/') !== false || 283 $this->check_for_file($url)) { 284 return $url; 285 } 286 287 return $this->add_language_code_to_link($language_code, $url); 288 } */ 289 290 private function getLanguageFromUrl($url, $regex_lang) { 291 $pattern = '/\/(' . $regex_lang . ')(\/|$|\?|#)/'; 292 $language_code = null; 293 294 if (preg_match($pattern, wp_unslash($url), $matches)) { 295 $language_code = $matches[1] ?? null; 296 } 297 298 return $language_code; 299 } 217 300 } -
fluentc-translation/trunk/src/services/class-cache.php
r3152094 r3170433 99 99 } 100 100 101 102 /** 103 * Get multiple values from cache. 104 * 105 * @since 2.0 106 * 107 * @param array $keys Array of cache keys. 108 * @return array Associative array of cache key => cache value pairs. 109 */ 110 public function get_multiple(array $keys): array 111 { 112 $results = []; 113 foreach ($keys as $key) { 114 $results[$key] = $this->get($key); 115 } 116 return $results; 117 } 118 119 /** 120 * Set multiple values in cache. 121 * 122 * @since 2.0 123 * 124 * @param array $data Associative array of key => value pairs to cache. 125 * @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration). 126 * @return void 127 */ 128 public function set_multiple(array $data, int $expiration = 0): void 129 { 130 foreach ($data as $key => $value) { 131 if ($expiration > 0) { 132 $this->set_exp($key, $value, $expiration); 133 } else { 134 $this->set($key, $value); 135 } 136 } 137 } 138 139 101 140 /** 102 141 * Clean the cache … … 138 177 */ 139 178 public function delete_fluentc_transients() { 140 global $wpdb; // We need this to perform database operations.141 179 global $wpdb; 180 142 181 // Define the SQL query with placeholders for dynamic parts. 143 182 $sql = "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s"; 183 144 184 // Prepare the SQL statement with actual values to ensure safe queries. 145 $prepared_sql = $wpdb->prepare( $sql, '_transient_fluentc%', '_transient_timeout_fluentc%' ); 146 147 // Attempt to get the result from the cache first. 148 $cache_key = 'fluentc_transients'; 149 $transients = wp_cache_get( $cache_key ); 150 151 if ( false === $transients ) { 152 // Execute the query if not found in cache. 153 $transients = $wpdb->get_col( $prepared_sql ); 154 155 // Cache the result to optimize future loads. 156 wp_cache_set( $cache_key, $transients ); 157 } 158 185 $prepared_sql = $wpdb->prepare($sql, $wpdb->esc_like('_transient_fluentc') . '%', $wpdb->esc_like('_transient_timeout_fluentc') . '%'); 186 187 // Execute the query 188 $transients = $wpdb->get_col($prepared_sql); 189 190 if ($wpdb->last_error) { 191 error_log('FluentC: Error fetching transients: ' . $wpdb->last_error); 192 return; 193 } 194 159 195 // Loop through the found transients and delete them. 160 foreach ( $transients as $transient) {196 foreach ($transients as $transient) { 161 197 // Extract the transient name by removing the prefix. 162 $transient_name = str_replace( array( '_transient_timeout_', '_transient_' ), '', $transient);163 198 $transient_name = str_replace(array('_transient_timeout_', '_transient_'), '', $transient); 199 164 200 // Use WordPress function to delete the transient. 165 delete_transient( $transient_name ); 166 } 201 $deleted = delete_transient($transient_name); 202 203 if (!$deleted) { 204 error_log('FluentC: Failed to delete transient: ' . $transient_name); 205 } 206 } 207 208 // Clear the object cache for good measure 209 wp_cache_flush(); 167 210 } 168 211 -
fluentc-translation/trunk/src/services/class-connect.php
r3161147 r3170433 15 15 16 16 use FluentC\Models\Body; 17 use stdClass; 17 18 18 19 19 if ( ! defined( 'ABSPATH' ) ) { … … 24 24 * FluentC Connection Class 25 25 */ 26 class Connect { 27 28 29 /** 30 * Hold the class instance. 31 * 32 * @var object 33 */ 34 private static $instance = null; 35 /** 36 * FluentC Cache class 37 * 38 * @var object 39 */ 40 protected $fluentc_cache; 41 42 /** 43 * FluentC remote url 44 * 45 * @var string 46 */ 47 protected $fluentc_remote_url; 48 49 /** 50 * Constructor. 51 * 52 * @since 1.2 53 */ 54 public function __construct() { 55 $this->fluentc_cache = new Cache(); 56 $this->fluentc_remote_url = 'https://dashboard.fluentc.ai/graphql'; 57 } 58 59 60 /** 61 * Retrieve Language List as ISO Codes 62 * 63 * @return array 64 * @since 1.2 65 * @param string $widget_id Site widget id. 66 */ 67 public function get_language_list( $widget_id ) { 68 69 $languages = array(); 70 if ( $this->fluentc_cache->get( 'fluentc_language_list' ) ) { 71 $languages = $this->fluentc_cache->get( 'fluentc_language_list' ); 72 } else { 73 74 $widgetapikey = get_option( 'fluentc_api_key' ); 75 if ( $widgetapikey ) { 76 77 $widget = $this->fetch_widget_options( $widget_id ); 78 79 $get_all_languages = $this->get_available_languages( $widget_id ); 80 81 foreach ( $get_all_languages->data->getAvailableLanguages->body as $key => $value ) { 82 $languages[] = $value->code; 83 } 84 // fetch available languages Environment ID. 85 $this->fluentc_cache->set_exp( 'fluentc_language_list', $languages, 43200 ); 86 do_action( 'fluentc_activation_setup' ); 87 } 88 } 89 return $languages; 90 } 91 92 93 /** 94 * Retrieve Language is with ISO Code and Name 95 * 96 * @return array 97 * @since 1.2 98 * @param string $widget_id Site ID. 99 */ 100 public function get_display_language_list( $widget_id ) { 101 102 $languages = array(); 103 if ( $this->fluentc_cache->get( 'fluentc_display_language_list' ) ) { 104 $languages = $this->fluentc_cache->get( 'fluentc_display_language_list' ); 105 } else { 106 107 $get_all_languages = $this->get_available_languages( $widget_id ); 108 109 foreach ( $get_all_languages->data->getAvailableLanguages->body as $key => $value ) { 110 $languages[] = array( $value->name, $value->code ); 111 } 112 113 $this->fluentc_cache->set_exp( 'fluentc_display_language_list', $languages , 43200 ); 114 do_action( 'fluentc_activation_setup' ); 115 } 116 return $languages; 117 } 26 class Connect 27 { 28 protected $fluentc_cache; 29 protected $fluentc_remote_url; 30 protected $api_key = 'da2-wtkl5bpofjbu5ex5iugu4o2mbm'; 31 32 public function __construct() 33 { 34 $this->fluentc_cache = new Cache(); 35 $this->fluentc_remote_url = 'https://dashboard.fluentc.ai/graphql'; 36 } 37 38 public function get_language_list($widget_id) 39 { 40 return $this->getCachedOrFetch('fluentc_language_list', function () use ($widget_id) { 41 $languages = []; 42 $get_all_languages = $this->get_available_languages($widget_id); 43 foreach ($get_all_languages->data->getAvailableLanguages->body as $value) { 44 $languages[] = $value->code; 45 } 46 do_action('fluentc_activation_setup'); 47 return $languages; 48 }, 43200); 49 } 50 51 public function get_display_language_list($widget_id) 52 { 53 return $this->getCachedOrFetch('fluentc_display_language_list', function () use ($widget_id) { 54 $languages = []; 55 $get_all_languages = $this->get_available_languages($widget_id); 56 foreach ($get_all_languages->data->getAvailableLanguages->body as $value) { 57 $languages[] = [$value->name, $value->code]; 58 } 59 do_action('fluentc_activation_setup'); 60 return $languages; 61 }, 43200); 62 } 63 64 /** 65 * Retrieves FluentC Languages as an array. 66 * 67 * This function fetches the available languages from FluentC, caches them, 68 * and applies any filtering based on the provided arguments. 69 * 70 * @param array $args Optional. Arguments to filter the languages. 71 * Supported args: 72 * - 'hide_empty' (bool) Whether to hide languages with no posts. 73 * - 'hide_default' (bool) Whether to hide the default language. 74 * - 'fields' (string|array) Specific fields to return. 75 * @return array|null An array of language objects, or null if no API key is set. 76 */ 77 public function get_fluentc_languages_list($args = []) 78 { 79 $widgetapikey = get_option('fluentc_api_key'); 80 if (!$widgetapikey) { 81 return null; 82 } 83 84 return $this->getCachedOrFetch('fluentc_array_language_list', function () use ($widgetapikey, $args) { 85 $get_all_languages = $this->get_available_languages($widgetapikey); 86 $languages = array_map(function ($language) { 87 return (object) [ 88 'name' => $language->name, 89 'slug' => $language->code, 90 'is_default' => false, 91 'count' => 0, 92 ]; 93 }, $get_all_languages->data->getAvailableLanguages->body); 94 95 return $this->filterLanguages($languages, $args); 96 }, 43200); 97 } 98 118 99 /** 119 * Retrieve FluentC Languages as an array 120 * 121 * @return array 122 * @since 1.2 123 * @param string $widget_id Widget ID. 124 */ 125 public function get_fluentc_languages_list( $args = array() ) { 126 // Check if the widget API key is set 127 $widgetapikey = get_option( 'fluentc_api_key' ); 128 if ( !$widgetapikey ) { 129 return; // Return nothing if the API key is not set 130 } 131 132 // Attempt to retrieve languages from cache. 133 $languages = $this->fluentc_cache->get( 'fluentc_array_language_list' ); 134 135 // If not cached, fetch and cache the languages. 136 if ( ! is_array( $languages ) ) { 137 // Fetch available languages from FluentC server. 138 $get_all_languages = $this->get_available_languages( $widgetapikey ); 139 140 $languages = array(); 141 foreach ( $get_all_languages->data->getAvailableLanguages->body as $language ) { 142 $languages[] = (object) array( 143 'name' => $language->name, 144 'slug' => $language->code, 145 'is_default' => false, // Adjust this if you have default language logic. 146 'count' => 0, // Placeholder for count if needed. 147 ); 148 } 149 150 // Cache the fetched languages. 151 $this->fluentc_cache->set_exp( 'fluentc_array_language_list', $languages , 43200 ); 152 } 153 154 // Filter the languages based on provided arguments. 155 $languages = array_filter( 156 $languages, 157 function ( $language ) use ( $args ) { 158 $keep_empty = empty( $args['hide_empty'] ) || $language->count; 159 $keep_default = empty( $args['hide_default'] ) || ! $language->is_default; 160 return $keep_empty && $keep_default; 161 } 162 ); 163 164 // Re-index the array. 165 $languages = array_values( $languages ); 166 167 // Return the requested fields or the full language objects. 168 return empty( $args['fields'] ) ? $languages : wp_list_pluck( $languages, $args['fields'] ); 169 } 170 171 /** 172 * Retrieve Site Options and connect to FluentC 173 * 174 * @return object 175 * @since 1.2 176 * @param string $widget_id Widget ID. 177 */ 178 public function fetch_widget_options( $widget_id ) { 179 180 $cache = $this->fluentc_cache->get( $widget_id ); 181 if ( $cache ) { 182 return json_decode( $cache ); 183 } else { 184 // Setup the endpoint URL. 185 $url = $this->fluentc_remote_url; 186 187 // Prepare the GraphQL query with the dynamic widgetID. 188 $data = array( 189 'query' => "{\n fetchSiteOptions(\n siteId: \"$widget_id\"\n ) {\n disabled\n name\n appearance\n sourceLanguage\n languages\n }\n}", 190 ); 191 $json_data = wp_json_encode( $data ); 192 193 // Set up the arguments for the request. 194 $args = array( 195 'body' => $json_data, 196 'headers' => array( 197 'Content-Type' => 'application/json', 198 'x-api-key' => 'da2-wtkl5bpofjbu5ex5iugu4o2mbm', 199 ), 200 'referer' => get_site_url(), 201 'method' => 'POST', 202 'data_format' => 'body', 203 'timeout' => 20, 204 ); 205 206 // Use WordPress function to execute the POST request. 207 $response = wp_remote_post( $url, $args ); 208 209 // Check for errors. 210 if ( is_wp_error( $response ) ) { 211 do_action( 'qm/error', 'Error in request: ' . $response->get_error_message() ); 212 213 return null; 214 } 215 216 // Retrieve the body of the response. 217 $body = wp_remote_retrieve_body( $response ); 218 $status = wp_remote_retrieve_response_code( $response ); 219 220 $response_data = json_decode( $body, true ); // decode to associative array for easier handling. 221 if ( ! $response_data ) { 222 do_action( 'qm/error', 'Invalid JSON response' ); 223 return null; 224 } 225 if ( 200 === $status ) { 226 // Assuming a successful response structure. Adjust according to your API. 227 if ( isset( $response_data['data'] ) && ! empty( $response_data['data'] ) ) { 228 // Process your data here. 229 $this->fluentc_cache->set_exp( $widget_id, $body, 43200 ); 230 } else { 231 do_action( 'qm/error', 'Data not found in response' ); 232 return null; 233 } 234 } else { 235 // Handle errors based on the response body. 236 if ( isset( $response_data['errors'] ) && is_array( $response_data['errors'] ) ) { 237 foreach ( $response_data['errors'] as $error ) { 238 if ( isset( $error['extensions']['code'] ) ) { 239 switch ( $error['extensions']['code'] ) { 240 case 'SITE_NOT_FOUND': 241 do_action( 'qm/error', 'Error: Site not found.' ); 242 // Handle site not found error. 243 break; 244 case 'LANGUAGE_NOT_ENABLED': 245 do_action( 'qm/error', 'Error: Language not enabled for site.' ); 246 // Handle language not enabled error. 247 break; 248 default: 249 do_action( 'qm/error', 'An unknown error occurred.' ); 250 // Handle other errors. 251 break; 252 } 253 } 254 } 255 } else { 256 do_action( 'qm/error', 'An unknown error occurred, but no error details were found' ); 257 } 258 return null; 259 } 260 // Return the response. 261 return json_decode( $body ); 262 } 263 } 264 /** 265 * Retrieve Available Language by Site Key 266 * 267 * @return object 268 * @since 1.2 269 * @param string $environment_id FluentC Environment ID. 270 */ 271 public function get_available_languages( $environment_id ) { 272 // Setup the endpoint URL. 273 $url = $this->fluentc_remote_url; 274 275 // Prepare the GraphQL query with the dynamic widgetID. 276 $data = array( 277 'query' => "{\n getAvailableLanguages(\n siteId: \"$environment_id\"\n targetLanguage:\"en\"\n ) {\n body\n {\n name\n code\n }}\n}", 278 ); 279 $json_data = wp_json_encode( $data ); 280 281 // Set up the arguments for the request. 282 $args = array( 283 'body' => $json_data, 284 'headers' => array( 285 'Content-Type' => 'application/json', 286 'x-api-key' => 'da2-wtkl5bpofjbu5ex5iugu4o2mbm', 287 ), 288 'referer' => get_site_url(), 289 'method' => 'POST', 290 'data_format' => 'body', 291 ); 292 293 // Use WordPress function to execute the POST request. 294 $response = wp_remote_post( $url, $args ); 295 296 // Check for errors. 297 if ( is_wp_error( $response ) ) { 298 do_action( 'qm/error', 'Error in request: ' . $response->get_error_message() ); 299 return null; 300 } 301 302 // Retrieve the body of the response. 303 $body = wp_remote_retrieve_body( $response ); 304 $status = wp_remote_retrieve_response_code( $response ); 305 306 $response_data = json_decode( $body, true ); // decode to associative array for easier handling. 307 if ( ! $response_data ) { 308 do_action( 'qm/error', 'Invalid JSON response' ); 309 return null; 310 } 311 if ( 200 === $status ) { 312 // Assuming a successful response structure. Adjust according to your API. 313 if ( isset( $response_data['data'] ) && ! empty( $response_data['data'] ) ) { 314 // Process your data here. 315 $this->fluentc_cache->set_exp( 'fluentc_' . $environment_id, $body, 43200 ); 316 } else { 317 do_action( 'qm/error', 'Data not found in response' ); 318 return null; 319 } 320 } else { 321 // Handle errors based on the response body. 322 if ( isset( $response_data['errors'] ) && is_array( $response_data['errors'] ) ) { 323 foreach ( $response_data['errors'] as $error ) { 324 if ( isset( $error['extensions']['code'] ) ) { 325 switch ( $error['extensions']['code'] ) { 326 case 'SITE_NOT_FOUND': 327 do_action( 'qm/error', 'Error: Site not found.' ); 328 // Handle site not found error. 329 break; 330 case 'LANGUAGE_NOT_ENABLED': 331 do_action( 'qm/error', 'Error: Language not enabled for site.' ); 332 // Handle language not enabled error. 333 break; 334 default: 335 do_action( 'qm/error', 'An unknown error occurred.' ); 336 // Handle other errors. 337 break; 338 } 339 } 340 } 341 } else { 342 do_action( 'qm/error', 'An unknown error occurred, but no error details were found' ); 343 } 344 return null; 345 } 346 // Return the response. 347 return json_decode( $body ); 348 } 349 /** 350 * Translates Text that is not expected to have HTML 351 * 352 * @return object 353 * @since 1.2 354 * @param string $widget_id FluentC Site ID. 355 * @param string $source_language Source Language. 356 * @param string $target_language Target Language. 357 * @param string $text Text to be translated. 358 * @param int $id id of post. 359 */ 360 public function get_translation_text( $widget_id, $source_language, $target_language, $text, $id = null ) { 361 if ( $id ) { 362 $key = $id; 363 } else { 364 $key = hash( 'crc32', $text ); 365 } 366 $cache = $this->fluentc_cache->get( $source_language . $target_language . $key ); 367 if ( $cache ) { 368 return json_decode( $cache ); 369 } else { 370 371 // Setup the endpoint URL. 372 $url = $this->fluentc_remote_url; 373 $text = addcslashes( $text, '"' ); 374 // Prepare the GraphQL query with the dynamic widgetID. 375 $data = array( 376 'query' => "{\n translateSite(\n siteId: \"$widget_id\" \n sourceLanguage: \"$source_language\" \n targetLanguage: \"$target_language\" \n labels: [\"$text\"] \n ) {\n body { \n sourceLanguage \n targetLanguage \n originalText \n translatedText \n }\n }\n }", 377 ); 378 $json_data = wp_json_encode( $data ); 379 380 // Set up the arguments for the request. 381 $args = array( 382 'body' => $json_data, 383 'headers' => array( 384 'Content-Type' => 'application/json', 385 'x-api-key' => 'da2-wtkl5bpofjbu5ex5iugu4o2mbm', 386 ), 387 'referer' => get_site_url(), 388 'method' => 'POST', 389 'data_format' => 'body', 390 ); 391 392 // Use WordPress function to execute the POST request. 393 $response = wp_remote_post( $url, $args ); 394 $apicalls = $this->fluentc_cache->get( 'apicalls' ); 395 $this->fluentc_cache->set( 'apicalls', $apicalls + 1 ); 396 // Check for errors. 397 if ( is_wp_error( $response ) ) { 398 do_action( 'qm/error', 'Error in request: ' . $response->get_error_message() ); 399 return null; 400 } 401 402 // Retrieve the body of the response. 403 $body = wp_remote_retrieve_body( $response ); 404 $status = wp_remote_retrieve_response_code( $response ); 405 406 $response_data = json_decode( $body, true ); // decode to associative array for easier handling. 407 if ( ! $response_data ) { 408 do_action( 'qm/error', 'Invalid JSON response' ); 409 return null; 410 } 411 412 if ( 200 === $status ) { 413 // Assuming a successful response structure. Adjust according to your API. 414 if ( isset( $response_data['data'] ) && ! empty( $response_data['data'] ) ) { 415 // Process your data here. 416 $this->fluentc_cache->set( $source_language . $target_language . $key, $body ); 417 } else { 418 do_action( 'qm/error', 'get_translation_text: Data not found in response' ); 419 return null; 420 } 421 } else { 422 // Handle errors based on the response body. 423 if ( isset( $response_data['errors'] ) && is_array( $response_data['errors'] ) ) { 424 foreach ( $response_data['errors'] as $error ) { 425 if ( isset( $error['extensions']['code'] ) ) { 426 switch ( $error['extensions']['code'] ) { 427 case 'SITE_NOT_FOUND': 428 do_action( 'qm/error', 'Error: Site not found.' ); 429 // Handle site not found error. 430 break; 431 case 'LANGUAGE_NOT_ENABLED': 432 do_action( 'qm/error', 'Error: Language not enabled for site.' ); 433 // Handle language not enabled error. 434 break; 435 default: 436 do_action( 'qm/error', 'An unknown error occurred.' ); 437 // Handle other errors. 438 break; 439 } 440 } 441 } 442 } else { 443 do_action( 'qm/error', 'An unknown error occurred, but no error details were found' ); 444 } 445 return null; 446 } 447 448 // Return the response. 449 return json_decode( $body ); 450 } 451 } 452 /** 453 * Translates Text that is expected to have HTML 454 * 455 * Return json_decode() 456 * 457 * @var mixed 458 * @since 1.2 459 * @param string $widget_id FluentC Site ID. 460 * @param string $source_language Source Language. 461 * @param string $target_language Target Language. 462 * @param string $text Text to be translated. 463 * @param array $text_to_translate Text to be translated. 464 * @param int $id id of post. 465 */ 466 public function get_translation_content($widget_id, $source_language, $target_language, $text_labels) { 467 // Ensure text_labels is properly formatted for GraphQL 468 if (is_string($text_labels)) { 469 // If it's a string, assume it's already formatted for GraphQL 470 $labels_string = $text_labels; 471 } elseif (is_array($text_labels)) { 472 // If it's an array, format it for GraphQL 473 $formatted_labels = array_map(function($label) { 474 return '"' . addslashes($label) . '"'; 475 }, $text_labels); 476 $labels_string = '[' . implode(', ', $formatted_labels) . ']'; 477 } else { 100 * Filters the languages based on provided arguments. 101 * 102 * @param array $languages Array of language objects to filter. 103 * @param array $args Arguments for filtering. See get_fluentc_languages_list() for details. 104 * @return array Filtered array of language objects. 105 */ 106 private function filterLanguages($languages, $args) 107 { 108 $languages = array_filter($languages, function ($language) use ($args) { 109 $keep_empty = empty($args['hide_empty']) || $language->count; 110 $keep_default = empty($args['hide_default']) || !$language->is_default; 111 return $keep_empty && $keep_default; 112 }); 113 114 $languages = array_values($languages); 115 116 return empty($args['fields']) ? $languages : wp_list_pluck($languages, $args['fields']); 117 } 118 119 public function fetch_widget_options($widget_id) 120 { 121 return $this->getCachedOrFetch($widget_id, function () use ($widget_id) { 122 return $this->makeGraphQLRequest( 123 "{\n fetchSiteOptions(\n siteId: \"$widget_id\"\n ) {\n disabled\n name\n appearance\n sourceLanguage\n languages\n }\n}" 124 ); 125 }, 43200); 126 } 127 128 public function get_available_languages($environment_id) 129 { 130 return $this->makeGraphQLRequest( 131 "{\n getAvailableLanguages(\n siteId: \"$environment_id\"\n targetLanguage:\"en\"\n ) {\n body\n {\n name\n code\n }}\n}" 132 ); 133 } 134 135 public function get_translation_text($widget_id, $source_language, $target_language, $text, $id = null) 136 { 137 $key = $id ?: hash('crc32', $text); 138 return $this->getCachedOrFetch($source_language . $target_language . $key, function () use ($widget_id, $source_language, $target_language, $text) { 139 $escapedText = addcslashes($text, '"'); 140 return $this->makeGraphQLRequest( 141 "{\n translateSite(\n siteId: \"$widget_id\" \n sourceLanguage: \"$source_language\" \n targetLanguage: \"$target_language\" \n labels: [\"$escapedText\"] \n ) {\n body { \n sourceLanguage \n targetLanguage \n originalText \n translatedText \n }\n }\n }" 142 ); 143 }); 144 } 145 146 /** 147 * Get translation content from the API. 148 * 149 * @param string $widget_id The widget ID. 150 * @param string $source_language The source language code. 151 * @param string $target_language The target language code. 152 * @param array $text_labels The text labels to translate. 153 * @return array The translated content. 154 */ 155 public function get_translation_content($widget_id, $source_language, $target_language, $text_labels) 156 { 157 $labels_string = $this->formatLabels($text_labels); 158 $query = $this->buildTranslationQuery($widget_id, $source_language, $target_language, $labels_string); 159 160 do_action('qm/debug', 'GraphQL Query: ' . $query); 161 162 $response = $this->makeGraphQLRequest($query); 163 $this->fluentc_cache->set('apicalls', $this->fluentc_cache->get('apicalls') + 1); 164 165 if (!$response) { 166 do_action('qm/error', 'Translation error: No response from API'); 167 return []; 168 } 169 170 if (!isset($response->data->translateSite->body)) { 171 do_action('qm/error', 'Translation error: Unexpected response structure'); 172 do_action('qm/debug', 'API Response: ' . json_encode($response)); 173 return []; 174 } 175 176 return $this->processTranslationResponse($response); 177 } 178 179 /** 180 * Get translation content from the API. 181 * 182 * @param string $widget_id The widget ID. 183 * @param string $source_language The source language code. 184 * @param string $target_language The target language code. 185 * @param array $text_labels The text labels to translate. 186 * @return array The translated content. 187 */ 188 public function get_translation_content_with_placeholders($widget_id, $source_language, $target_language, $text_array) 189 { 190 $labels_array = $this->convertTranslationArrayFormat($text_array); 191 $query = $this->buildTranslationQueryWithPlaceholders($widget_id, $source_language, $target_language, $labels_array); 192 193 do_action('qm/debug', 'GraphQL Query: ' . $query); 194 195 $response = $this->makeGraphQLRequest($query); 196 $this->fluentc_cache->set('apicalls', $this->fluentc_cache->get('apicalls') + 1); 197 198 if (!$response) { 199 do_action('qm/error', 'Translation error: No response from API'); 200 return []; 201 } 202 203 if (!isset($response->data->translateWithPlaceholder->body)) { 204 do_action('qm/error', 'Translation error: Unexpected response structure'); 205 do_action('qm/debug', 'API Response: ' . json_encode($response)); 206 return []; 207 } 208 209 return $this->processTranslationWithPlaceholderResponse($response); 210 } 211 212 private function formatLabels($text_labels) 213 { 214 if (is_string($text_labels)) { 215 return str_replace('"', '"', trim($text_labels)); 216 } 217 if (is_array($text_labels)) { 218 return '[' . implode(', ', array_map(function($label) { 219 return '"' . str_replace('"', '"', trim($label)) . '"'; 220 }, $text_labels)) . ']'; 221 } 478 222 do_action('qm/error', 'Invalid text_labels format'); 479 223 return null; 480 224 } 481 225 482 // Prepare GraphQL query 483 $query = <<<GRAPHQL 484 { 485 translateSite( 486 siteId: "$widget_id" 487 sourceLanguage: "$source_language" 488 targetLanguage: "$target_language" 489 labels: [$labels_string] 490 ) { 491 body { 492 sourceLanguage 493 targetLanguage 494 originalText 495 translatedText 496 } 497 } 498 } 499 GRAPHQL; 500 501 // Log the query for debugging 502 do_action('qm/debug', 'GraphQL Query: ' . $query); 503 504 // Set up the request 505 $args = [ 506 'body' => wp_json_encode(['query' => $query]), 507 'headers' => [ 508 'Content-Type' => 'application/json', 509 'x-api-key' => 'da2-wtkl5bpofjbu5ex5iugu4o2mbm', 510 ], 511 'referer' => get_site_url(), 512 'method' => 'POST', 513 'timeout' => 20, 514 ]; 515 516 // Execute the request 517 $response = wp_remote_post($this->fluentc_remote_url, $args); 518 $this->fluentc_cache->set('apicalls', $this->fluentc_cache->get('apicalls') + 1); 519 520 if (is_wp_error($response)) { 521 do_action('qm/error', 'Error in request: ' . $response->get_error_message()); 226 227 228 private function buildTranslationQuery($widget_id, $source_language, $target_language, $labels_string) 229 { 230 return <<<GRAPHQL 231 { 232 translateSite( 233 siteId: "$widget_id" 234 sourceLanguage: "$source_language" 235 targetLanguage: "$target_language" 236 labels: $labels_string 237 ) { 238 body { 239 sourceLanguage 240 targetLanguage 241 originalText 242 translatedText 243 } 244 } 245 } 246 GRAPHQL; 247 } 248 249 private function convertTranslationArrayFormat($translationArray) 250 { 251 $convertedArray = []; 252 253 foreach ($translationArray as $placeholder => $text) { 254 $convertedArray[] = [ 255 "label" => str_replace('"', '"', trim($text)), 256 "placeholder" => "$placeholder" 257 ]; 258 } 259 260 return $convertedArray; 261 } 262 private function buildTranslationQueryWithPlaceholders($widget_id, $source_language, $target_language, $labels_array) 263 { 264 // Convert the labels array to a JSON string, ensuring correct key names. 265 $labels_json = json_encode($labels_array, JSON_UNESCAPED_UNICODE); 266 267 // Remove the quotes around the key names. 268 $labels_json = preg_replace('/"([^"]+)"\s*:/', '$1:', $labels_json); 269 270 return <<<GRAPHQL 271 { 272 translateWithPlaceholder( 273 siteId: "$widget_id" 274 sourceLanguage: "$source_language" 275 targetLanguage: "$target_language" 276 labels: $labels_json 277 ) { 278 body { 279 targetLanguage 280 sourceLanguage 281 labels { 282 originalLabel 283 translatedLabel 284 originalPlaceholder 285 } 286 } 287 } 288 } 289 GRAPHQL; 290 } 291 292 private function processTranslationResponse($response) 293 { 294 $translations = []; 295 foreach ($response->data->translateSite->body as $translation) { 296 $originalText = html_entity_decode($translation->originalText, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 297 $translatedText = html_entity_decode($translation->translatedText, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 298 $translations[$originalText] = $translatedText; 299 } 300 return $translations; 301 } 302 303 private function processTranslationWithPlaceholderResponse($response) 304 { 305 $translations = []; 306 foreach ($response->data->translateWithPlaceholder->body->labels as $translation) { 307 $placeholder = $translation->originalPlaceholder; 308 $originalLabel = $translation->originalLabel; 309 $translatedLabel = html_entity_decode($translation->translatedLabel, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 310 311 $translations[$placeholder] = [ 312 'originalLabel' => $originalLabel, 313 'translatedLabel' => $translatedLabel, 314 'originalPlaceholder' => $placeholder 315 ]; 316 } 317 return $translations; 318 } 319 320 public function get_language_list_string($widget_id) 321 { 322 $languages = $this->get_language_list($widget_id); 323 return empty($languages) ? '' : implode('|', $languages); 324 } 325 326 public function heartbeat($widget_id = null) 327 { 328 $site_url = $this->getSiteUrl(); 329 $site_detail = $widget_id ? 'siteId: "' . $widget_id . '" ' : ''; 330 331 $query = <<<GRAPHQL 332 { 333 heartBeat( 334 host: "$site_url", 335 $site_detail 336 ) { 337 body { 338 status 339 } 340 } 341 } 342 GRAPHQL; 343 344 $response = $this->makeGraphQLRequest($query); 345 346 if ($response && isset($response->data)) { 347 do_action('qm/info', 'FluentC Site setup post completed'); 348 return $response; 349 } 350 522 351 return null; 523 352 } 524 353 525 $body = wp_remote_retrieve_body($response); 526 $status = wp_remote_retrieve_response_code($response); 527 528 // Log the raw response for debugging 529 do_action('qm/debug', 'Raw API Response: ' . $body); 530 531 $response_data = json_decode($body, true); 532 533 if (!$response_data) { 534 do_action('qm/error', 'Invalid JSON response'); 535 return null; 536 } 537 538 if ($status === 200) { 539 if (isset($response_data['data']) && !empty($response_data['data']['translateSite']['body'])) { 540 $body_array = []; 541 foreach ($response_data['data']['translateSite']['body'] as $translatedText) { 542 $original_text = $translatedText['originalText']; 543 $translated_text = $translatedText['translatedText']; 544 $sourceLanguage = $translatedText['sourceLanguage']; 545 $targetLanguage = $translatedText['targetLanguage']; 546 547 $text_to_cache = new Body($original_text, $translated_text, $sourceLanguage, $targetLanguage); 548 /*$key = hash('md5', $original_text); 549 $cache_key = $source_language . $target_language . $key; 550 $this->fluentc_cache->set($cache_key, json_encode([ 551 'data' => ['translateSite' => ['body' => $text_to_cache]] 552 ]));*/ 553 554 $body_array[] = $text_to_cache; 555 } 556 557 $response_object = new \stdClass(); 558 $response_object->data = new \stdClass(); 559 $response_object->data->translateSite = new \stdClass(); 560 $response_object->data->translateSite->body = $body_array; 561 562 return $response_object; 563 } else { 564 do_action('qm/error', 'Translation error: Data not found in response. Response structure: ' . print_r($response_data, true)); 565 } 566 } else { 567 $this->handleTranslationError($response_data); 568 } 569 570 return null; 571 } 572 private function handleTranslationError($response_data) { 573 if (isset($response_data['errors']) && is_array($response_data['errors'])) { 574 foreach ($response_data['errors'] as $error) { 575 do_action('qm/error', 'GraphQL Error: ' . json_encode($error)); 576 if (isset($error['message'])) { 577 do_action('qm/error', 'Error message: ' . $error['message']); 578 } 579 if (isset($error['extensions']['code'])) { 580 switch ($error['extensions']['code']) { 581 case 'SITE_NOT_FOUND': 582 do_action('qm/error', 'Error: Site not found.'); 583 break; 584 case 'LANGUAGE_NOT_ENABLED': 585 do_action('qm/error', 'Error: Language not enabled for site.'); 586 break; 587 default: 588 do_action('qm/error', 'An unknown error occurred. Code: ' . $error['extensions']['code']); 589 break; 590 } 591 } 592 } 593 } else { 594 do_action('qm/error', 'An unknown error occurred, but no error details were found. Response: ' . print_r($response_data, true)); 595 } 596 } 354 private function getSiteUrl() 355 { 356 $site_language = get_bloginfo('url'); 357 $url_parts = wp_parse_url($site_language); 358 return $url_parts['scheme'] . '://' . $url_parts['host']; 359 } 360 597 361 /** 598 * Retrieves Language list in regex format599 * 600 * @ return string601 * @ since 1.2602 * @ param string $widget_id Widget ID.362 * Summary of getCachedOrFetch 363 * @param mixed $key 364 * @param mixed $fetchCallback 365 * @param mixed $expiration 366 * @return mixed 603 367 */ 604 public function get_language_list_string( $widget_id ) { 605 $languages = $this->get_language_list( $widget_id ); // Assuming you have access to this method. 606 607 // If the language list is empty, return an empty string. 608 if ( empty( $languages ) ) { 609 return ''; 610 } 611 612 // If the language list is not empty, format it as desired. 613 return implode( '|', $languages ); 614 } 615 616 /** 617 * Sets up connection to FluentC 618 * 619 * @return object 620 * @since 1.3 621 * @param string $widget_id Widget ID. 622 */ 623 public function heartbeat( $widget_id = null ) { 624 625 // Setup the endpoint URL. 626 $url = $this->fluentc_remote_url; 627 $site_language = get_bloginfo( 'url' ); 628 $url_parts = wp_parse_url( $site_language ); 629 630 // Rebuild the URL with the language code. 631 $site_url = $url_parts['scheme'] . '://' . $url_parts['host']; 632 do_action( 'qm/debug', '$widget_id: ' . $widget_id ); 633 if ( $widget_id ) { 634 $site_detail = 'siteId: "'.$widget_id.'" '; 635 } else { 636 $site_detail = ''; 637 } 638 //'query' => "{\n heartBeat( \n host: \"$site_url\" ) {\n body { \n status\n } \n}", 639 // Prepare the GraphQL query with the dynamic widgetID. 640 $data = array( 641 "query" => "{\n heartBeat(\n host: \"$site_url\",\n $site_detail ) {\n body {\n status\n }\n }\n}" 642 ); 643 $json_data = wp_json_encode( $data ); 644 645 // Set up the arguments for the request. 646 $args = array( 647 'body' => $json_data, 648 'headers' => array( 649 'Content-Type' => 'application/json', 650 'x-api-key' => 'da2-wtkl5bpofjbu5ex5iugu4o2mbm', 651 ), 652 'referer' => get_site_url(), 653 'method' => 'POST', 654 'data_format' => 'body', 655 'timeout' => 20, 656 ); 657 658 // Use WordPress function to execute the POST request. 659 $response = wp_remote_post( $url, $args ); 660 661 // Check for errors. 662 if ( is_wp_error( $response ) ) { 663 do_action( 'qm/error', 'Error in request: ' . $response->get_error_message() ); 664 665 return null; 666 } 667 668 // Retrieve the body of the response. 669 $body = wp_remote_retrieve_body( $response ); 670 $status = wp_remote_retrieve_response_code( $response ); 671 672 $response_data = json_decode( $body, true ); // decode to associative array for easier handling. 673 if ( ! $response_data ) { 674 do_action( 'qm/error', 'Invalid JSON response' ); 675 return null; 676 } 677 if ( 200 === $status ) { 678 // Assuming a successful response structure. Adjust according to your API. 679 if ( isset( $response_data['data'] ) && ! empty( $response_data['data'] ) ) { 680 // Process your data here. 681 do_action( 'qm/info', 'Data found in response' ); 682 } else { 683 do_action( 'qm/error', 'Data not found in response' ); 684 return null; 685 } 686 } else { 687 // Handle errors based on the response body. 688 if ( isset( $response_data['errors'] ) && is_array( $response_data['errors'] ) ) { 689 foreach ( $response_data['errors'] as $error ) { 690 if ( isset( $error['extensions']['code'] ) ) { 691 switch ( $error['extensions']['code'] ) { 692 case 'SITE_NOT_FOUND': 693 do_action( 'qm/debu', 'Error: Site not found.' ); 694 // Handle site not found error. 695 break; 696 case 'LANGUAGE_NOT_ENABLED': 697 do_action( 'qm/error', 'Error: Language not enabled for site.' ); 698 // Handle language not enabled error. 699 break; 700 default: 701 do_action( 'qm/error', 'An unknown error occurred.' ); 702 // Handle other errors. 703 break; 704 } 705 } 706 } 707 } else { 708 do_action( 'qm/error', 'An unknown error occurred, but no error details were found' ); 709 } 710 return null; 711 } 712 do_action( 'qm/info', 'FluentC Site setup post completed' ); 713 // Return the response. 714 return json_decode( $body ); 715 } 716 } 368 private function getCachedOrFetch($key, $fetchCallback, $expiration = 0) 369 { 370 $cached = $this->fluentc_cache->get($key); 371 if ($cached !== false) { 372 return is_string($cached) ? json_decode($cached) : $cached; 373 } 374 375 $data = $fetchCallback(); 376 if ($data !== null) { 377 $this->fluentc_cache->set_exp($key, is_string($data) ? $data : json_encode($data), $expiration); 378 } 379 380 return $data; 381 } 382 383 public function get_pll_fluentc_languages_list($args = []) 384 { 385 $widgetapikey = get_option('fluentc_api_key'); 386 if (!$widgetapikey) { 387 return []; // Return an empty array instead of null 388 } 389 390 return $this->getPLLCachedOrFetch('fluentc_array_language_list', function () use ($widgetapikey, $args) { 391 $get_all_languages = $this->get_available_languages($widgetapikey); 392 393 // Check if the API call was successful 394 if (!$get_all_languages || !isset($get_all_languages->data->getAvailableLanguages->body)) { 395 return []; // Return an empty array if there's an error 396 } 397 398 $languages = array_map(function ($language) { 399 return (object) [ 400 'name' => $language->name, 401 'slug' => $language->code, 402 'is_default' => false, 403 'count' => 0, 404 ]; 405 }, $get_all_languages->data->getAvailableLanguages->body); 406 407 return $this->filterLanguages($languages, $args); 408 }, 43200); 409 } 410 411 private function getPLLCachedOrFetch($key, $fetchCallback, $expiration = 0) 412 { 413 $cached = $this->fluentc_cache->get($key); 414 if ($cached !== false) { 415 // Ensure we always return an array 416 $decoded = is_string($cached) ? json_decode($cached, true) : $cached; 417 return is_array($decoded) ? $decoded : []; 418 } 419 420 $data = $fetchCallback(); 421 if ($data !== null) { 422 $this->fluentc_cache->set_exp($key, json_encode($data), $expiration); 423 } 424 425 // Ensure we always return an array 426 return is_array($data) ? $data : []; 427 } 428 429 private function makeGraphQLRequest($query) 430 { 431 $response = wp_remote_post($this->fluentc_remote_url, [ 432 'body' => wp_json_encode(['query' => $query]), 433 'headers' => [ 434 'Content-Type' => 'application/json', 435 'x-api-key' => $this->api_key, 436 ], 437 'timeout' => 20, 438 ]); 439 440 if (is_wp_error($response)) { 441 do_action('qm/error', 'Error in request: ' . $response->get_error_message()); 442 return null; 443 } 444 445 $body = wp_remote_retrieve_body($response); 446 $status = wp_remote_retrieve_response_code($response); 447 448 $response_data = json_decode($body); 449 450 if ($status !== 200 || !$response_data) { 451 $this->handleRequestError($status, $response_data); 452 return null; 453 } 454 455 return $response_data; 456 } 457 458 private function handleRequestError($status, $response_data) 459 { 460 if ($status !== 200) { 461 do_action('qm/error', "HTTP Error: $status"); 462 } 463 464 if (!$response_data) { 465 do_action('qm/error', 'Invalid JSON response'); 466 return; 467 } 468 469 if (isset($response_data->errors)) { 470 foreach ($response_data->errors as $error) { 471 $code = $error->extensions->code ?? 'UNKNOWN_ERROR'; 472 $message = $error->message ?? 'An unknown error occurred'; 473 do_action('qm/error', "GraphQL Error ($code): $message"); 474 } 475 } 476 } 477 } -
fluentc-translation/trunk/src/services/class-widget.php
r3152094 r3170433 101 101 $header_code .= ' f = new fluentcWidgetWordpress({ 102 102 defaultLanguage: \'' . $site_language . '\', 103 display: "' . $apperance . '", // dropdown, float, list103 display: "' . $apperance . '", 104 104 languages: [' . $language_string . '] 105 105 ' . $init_lang . ', -
fluentc-translation/trunk/vendor/fluentc/php-html-parser/src/PHPHtmlParser/Content.php
r3155849 r3170433 14 14 class Content 15 15 { 16 16 17 /** 17 18 * The content string. … … 111 112 { 112 113 return \strlen($this->content) >= $this->pos + $count; 114 } 115 116 /** 117 * Copies the remainder of the content from the current position to the end. 118 * 119 * @return string The remaining content. 120 */ 121 public function copyRemainder(): string 122 { 123 if ($this->isEof()) { 124 return ''; 125 } 126 127 $remainder = substr($this->content, $this->pos); 128 $this->pos = strlen($this->content); 129 return $remainder; 130 } 131 132 /** 133 * Checks if the current position is at or past the end of the content. 134 * 135 * @return bool True if at the end of file, false otherwise. 136 */ 137 public function isEof(): bool 138 { 139 return $this->pos >= $this->size; 113 140 } 114 141 -
fluentc-translation/trunk/vendor/fluentc/php-html-parser/src/PHPHtmlParser/Dom/Node/AbstractNode.php
r3155849 r3170433 106 106 { 107 107 // check attribute first 108 if ($this-> getAttribute($key) !== null) {108 if ($this->tag !== null && $this->getAttribute($key) !== null) { 109 109 return $this->getAttribute($key); 110 110 } … … 123 123 return $this->getParent(); 124 124 } 125 }126 125 return null; 126 } 127 127 /** 128 128 * Simply calls the outer text method. … … 299 299 * Gets the tag object of this node. 300 300 */ 301 public function getTag(): Tag301 public function getTag(): ?Tag 302 302 { 303 303 return $this->tag; 304 304 } 305 306 305 307 306 308 /** … … 343 345 public function getAttribute(string $key): ?string 344 346 { 347 if ($this->tag === null) { 348 return null; 349 } 345 350 try { 346 351 $attributeDTO = $this->tag->getAttribute($key); 347 352 } catch (AttributeNotFoundException $e) { 348 353 // no attribute with this key exists, returning null. 349 unset($e);350 351 354 return null; 352 355 } -
fluentc-translation/trunk/vendor/fluentc/php-html-parser/src/PHPHtmlParser/Dom/Node/Collection.php
r3162981 r3170433 121 121 * @param mixed $offset 122 122 */ 123 #[\ReturnTypeWillChange] 123 124 public function offsetUnset($offset) 124 125 { -
fluentc-translation/trunk/vendor/fluentc/php-html-parser/src/PHPHtmlParser/Dom/Node/HtmlNode.php
r3162981 r3170433 146 146 147 147 // Skip generating tags for the dummy root. 148 if ($this->tag->name() === ' fluentc-root') {148 if ($this->tag->name() === 'root') { 149 149 return $this->innerHtml(); 150 150 }
Note: See TracChangeset
for help on using the changeset viewer.