Файловый менеджер - Редактировать - /var/www/portal.bdu.ac.bd/public_html/wp-content/plugins/translatepress-multilingual/includes/class-translation-render.php
Назад
<?php /** * Class TRP_Translation_Render * * Translates pages. */ class TRP_Translation_Render{ protected $settings; protected $machine_translator; /* @var TRP_Query */ protected $trp_query; /* @var TRP_Url_Converter */ protected $url_converter; /* @var TRP_Translation_Manager */ protected $translation_manager; protected $common_html_tags; /** * TRP_Translation_Render constructor. * * @param array $settings Settings options. */ public function __construct( $settings ){ $this->settings = $settings; // apply_filters only once instead of everytime is_html() is used $this->common_html_tags = implode( '|', apply_filters('trp_common_html_tags', array( 'html', 'body', 'table', 'tbody', 'thead', 'th', 'td', 'tr', 'div', 'p', 'span', 'b', 'a', 'strong', 'center', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img' ) ) ); } /** * Start Output buffer to translate page. */ public function start_output_buffer(){ global $TRP_LANGUAGE; //when we check if is an ajax request in frontend we also set proper REQUEST variables and language global so we need to run this for every buffer $ajax_on_frontend = TRP_Gettext_Manager::is_ajax_on_frontend();//TODO refactor this function si it just checks and does not set variables if( ( is_admin() && !$ajax_on_frontend ) || trp_is_translation_editor( 'true' ) ){ return;//we have two cases where we don't do anything: we are on the admin side and we are not in an ajax call or we are in the left side of the translation editor } else { global $trp_output_buffer_started;//use this global so we know that we started the output buffer. we can check it for instance when wrapping gettext mb_http_output("UTF-8"); if ( $TRP_LANGUAGE == $this->settings['default-language'] && !trp_is_translation_editor() ) { // on default language when we are not in editor we just need to clear any trp tags that could still be present and handle links for special situation $chunk_size = ($this->handle_custom_links_for_default_language() ) ? 0 : 4096; $chunk_size = apply_filters("trp_output_buffer_chunk_size", $chunk_size); ob_start(array( $this, 'render_default_language' ), $chunk_size); $trp_output_buffer_started = true; } else { ob_start(array($this, 'translate_page'));//everywhere else translate the page $trp_output_buffer_started = true; } } } /** * Function to hide php errors and notice and instead log them in debug.log so we don't store the notice strings inside the db if WP_DEBUG is on */ public function trp_debug_mode_off(){ if ( WP_DEBUG ) { ini_set('display_errors', 0); ini_set('log_errors', 1); ini_set('error_log', WP_CONTENT_DIR . '/debug.log'); } } /** * Forces the language to be the first non default one in the preview translation editor. * We're doing this because we need the ID's. * Otherwise we're just returning the global $TRP_LANGUAGE * * @return string Language code. */ protected function force_language_in_preview(){ global $TRP_LANGUAGE; if ( in_array( $TRP_LANGUAGE, $this->settings['translation-languages'] ) ) { if ( $TRP_LANGUAGE == $this->settings['default-language'] ){ // in the translation editor we need a different language then the default because we need string ID's. // so we're forcing it to the first translation language because if it's the default, we're just returning the $output if ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ) { if( count( $this->settings['translation-languages'] ) > 1 ){ foreach ($this->settings['translation-languages'] as $language) { if ($language != $TRP_LANGUAGE) { // return the first language not default. only used for preview mode return $language; } } } else{ return $TRP_LANGUAGE; } } }else { return $TRP_LANGUAGE; } } return false; } /** * Trim strings. * This function is kept for backwards compatibility for earlier versions of SEO Pack Add-on * * @deprecated * @param string $string Raw string. * @return string Trimmed string. */ public function full_trim( $string ) { return trp_full_trim( $string ); } /** * Preview mode string category name for give node type. * * @param string $current_node_type Node type. * @return string Category name. */ protected function get_node_type_category( $current_node_type ){ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $string_groups = $this->translation_manager->string_groups(); $node_type_categories = apply_filters( 'trp_node_type_categories', array( $string_groups['metainformation'] => array( 'meta_desc', 'page_title', 'meta_desc_img' ), $string_groups['images'] => array( 'image_src' ) )); foreach( $node_type_categories as $category_name => $node_groups ){ if ( in_array( $current_node_type, $node_groups ) ){ return $category_name; } } return $string_groups['stringlist']; } /** * String description to be used in preview mode dropdown list of strings. * * @param object $current_node Current node. * @return string Node description. */ protected function get_node_description( $current_node ){ $node_type_descriptions = apply_filters( 'trp_node_type_descriptions', array( array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'description', 'description' => esc_html__( 'Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:title', 'description' => esc_html__( 'OG Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:site_name', 'description' => esc_html__( 'OG Site Name', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:description', 'description' => esc_html__( 'OG Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:image:alt', 'description' => esc_html__( 'OG Image Alt', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'twitter:title', 'description' => esc_html__( 'Twitter Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'twitter:description', 'description' => esc_html__( 'Twitter Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'twitter:image:alt', 'description' => esc_html__( 'Twitter Image Alt', 'translatepress-multilingual' ) ), array( 'type' => 'page_title', 'description' => esc_html__( 'Page Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'DC.Title', 'description' => esc_html__( 'Dublin Core Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'DC.Description', 'description' => esc_html__( 'Dublin Core Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc_img', 'attribute' => 'property', 'value' => 'og:image', 'description' => esc_html__( 'OG Image', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc_img', 'attribute' => 'property', 'value' => 'og:image:secure_url', 'description' => esc_html__( 'OG Image Secure URL', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc_img', 'attribute' => 'name', 'value' => 'twitter:image', 'description' => esc_html__( 'Twitter Image', 'translatepress-multilingual' ) ), )); foreach( $node_type_descriptions as $node_type_description ){ if ( isset( $node_type_description['attribute'] )) { $attribute = $node_type_description['attribute']; } if ( $current_node['type'] == $node_type_description['type'] && ( ( isset( $node_type_description['attribute'] ) && isset( $current_node['node']->$attribute ) && $current_node['node']->$attribute == $node_type_description['value'] ) || ( ! isset( $node_type_description['attribute'] ) ) ) ) { return $node_type_description['description']; } } return ''; } /** * Specific trim made for translation block string * * Problem especially for nbsp; which gets saved like that in DB. Then, in translation-render, the string arrives with nbsp; rendered to actual space character. * Used before inserting in db, and when trying to match on translation-render. * * wp_strip_all_tags was moved before html_entity_decode functions because quotes (& #039;) within text * within html tag attributes would be decoded into a quote ' and made wp_strip_tags to cut more text based on the incorrect html. * Example of html that broke before this change: <div title='Voir les détails de l'analyse de sécurité'></div> * * @param $string * * @return string */ public function trim_translation_block( $string ){ return preg_replace('/\s+/', ' ', html_entity_decode( htmlspecialchars_decode( wp_strip_all_tags(trp_full_trim( $string )), ENT_QUOTES ) ) ) ; } /** * Recursive function that checks if a DOM node contains certain tags or not * @param $row * @param $tags * @return bool */ public function check_children_for_tags( $row, $tags ){ foreach( $row->children as $child ){ if( in_array( $child->tag, $tags ) ){ return true; } else{ $this->check_children_for_tags( $child, $tags ); } } } /** * Return translation block if matches any existing translation block from db * * Return null if not found * * @param $row * @param $all_existing_translation_blocks * @param $merge_rules * * @return bool */ public function find_translation_block( $row, $all_existing_translation_blocks, $merge_rules ){ if ( in_array( $row->tag, $merge_rules['top_parents'] ) ){ //$row->innertext is very intensive on dom nodes that have a lot of children so we try here to eliminate as many as possible here // the ideea is that if a dom node contains any top parent tags for blocks it can't be a block itself so we skip it $skip = $this->check_children_for_tags( $row, $merge_rules['top_parents'] ); if( !$skip ) { $trimmed_inner_text = $this->trim_translation_block($row->innertext); foreach ($all_existing_translation_blocks as $existing_translation_block) { if ($existing_translation_block->trimmed_original == $trimmed_inner_text) { return $existing_translation_block; } } } } return null; } /** * Function that translates the content post title, site title and post content in oembed response * * @param $data * @param $post * @param $width * @param $height * * @return array */ public function oembed_response_data($data, $post, $width, $height ){ if ( !empty( $data )) { $translatable_items = apply_filters( 'trp_oembed_response_data_translatable_items', array('title', 'html', 'provider_name') ); foreach( $translatable_items as $item ){ if ( isset( $data[$item] ) ) { $data[$item] = $this->translate_page( $data[$item] ); } } } // Otherwise we incorrectly unescape the sequence to end CDATA from ']]>' to ']]>' breaking the xml. It needs to stay escaped in oembed response data. remove_filter( 'trp_before_translate_content', array( $this, 'handle_cdata'), 1000); return $data; } /** * Function that translates the content excerpt and post title in the REST API * @param $response * @return mixed */ public function handle_rest_api_translations($response){ if ( isset( $response->data ) ) { if ( isset( $response->data['name'] ) ){ $response->data['name'] = $this->translate_page( $response->data['name'] ); } if ( isset( $response->data['title'] ) && isset( $response->data['title']['rendered'] ) ) { $response->data['title']['rendered'] = $this->translate_page( $response->data['title']['rendered'] ); } if ( isset( $response->data['excerpt'] ) && isset( $response->data['excerpt']['rendered'] ) ) { $response->data['excerpt']['rendered'] = $this->translate_page( $response->data['excerpt']['rendered'] ); } if ( isset( $response->data['content'] ) && isset( $response->data['content']['rendered'] ) ) { $response->data['content']['rendered'] = $this->translate_page( $response->data['content']['rendered'] ); } } return $response; } /** * Apply translation filters for REST API response */ public function add_callbacks_for_translating_rest_api(){ $post_types = array_merge(["comment", "category"],get_post_types()); foreach ( $post_types as $post_type ) { add_filter( 'rest_prepare_'. $post_type, array( $this, 'handle_rest_api_translations' ) ); } } /** * Finding translateable strings and replacing with translations. * * Method called for output buffer. * * @param string $output Entire HTML page as string. * @return string Translated HTML page. */ public function translate_page( $output ){ if ( apply_filters( 'trp_stop_translating_page', false, $output ) ){ return $output; } global $TRP_HDOM_QUOTE_DEFAULT; $TRP_HDOM_QUOTE_DEFAULT = apply_filters('trp_hdom_quote_default_double_quotes', '"'); global $trp_editor_notices; /* replace our special tags so we have valid html */ $output = str_ireplace('#!trpst#', '<', $output); $output = str_ireplace('#!trpen#', '>', $output); $output = apply_filters('trp_before_translate_content', $output); if ( strlen( $output ) < 1 || $output == false ){ return $output; } if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } /* make sure we only translate on the rest_prepare_$post_type filter in REST requests and not the whole json */ /* in certain cases $wp_rewrite is null, so it trows a fatal error. This is just a quick fix. The actual issue is probably in WordPress core * see taskid #2pjped */ global $wp_rewrite; if( is_object($wp_rewrite) ) { if( strpos( $this->url_converter->cur_page_url(), get_rest_url() ) !== false && strpos( current_filter(), 'rest_prepare_' ) !== 0 && current_filter() !== 'oembed_response_data' ){ $trpremoved = $this->remove_trp_html_tags( $output ); return $trpremoved; } } /* don't do anything on xmlrpc.php */ if( strpos( $this->url_converter->cur_page_url(), 'xmlrpc.php' ) !== false ){ $trpremoved = $this->remove_trp_html_tags( $output ); return $trpremoved; } global $TRP_LANGUAGE; $language_code = $this->force_language_in_preview(); if ($language_code === false) { return $output; } if ( $language_code == $this->settings['default-language'] ){ // Don't translate regular strings (non-gettext) when we have no other translation languages except default language ( count( $this->settings['publish-languages'] ) > 1 ) $translate_normal_strings = false; }else{ $translate_normal_strings = true; } $translate_normal_strings = apply_filters( 'trp_translate_regular_strings', $translate_normal_strings ); $preview_mode = isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview'; $json_array = json_decode( $output, true ); /* If we have a json response we need to parse it and only translate the nodes that contain html * * Removed is_ajax_on_frontend() check because we need to capture custom ajax events. * Decided that if $output is json decodable it's a good enough check to handle it this way. * We have necessary checks so that we don't get to this point when is_admin(), or when language is default. */ if( $json_array && $json_array != $output ) { /* if it's one of our own ajax calls don't do nothing */ if ( ! empty( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 && $_REQUEST['action'] != 'trp_split_translation_block' ) return $output; //check if we have a json response if ( ! empty( $json_array ) ) { if( is_array( $json_array ) ) { array_walk_recursive($json_array, array($this, 'translate_json')); }else { $json_array = $this->translate_page($json_array); } } return trp_safe_json_encode( $json_array ); } /** * Tries to fix the HTML document. It is off by default. Use at own risk. * Solves the problem where a duplicate attribute inside a tag causes the plugin to remove the duplicated attribute and all the other attributes to the right of the it. */ if( apply_filters( 'trp_try_fixing_invalid_html', false ) ) { if( class_exists('DOMDocument') ) { $dom = new DOMDocument(); $dom->encoding = 'utf-8'; libxml_use_internal_errors(true);//so no warnings will show up for invalid html $dom->loadHTML(utf8_decode($output), LIBXML_NOWARNING | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); $output = $dom->saveHTML(); } } $no_translate_attribute = 'data-no-translation'; $no_auto_translate_attribute = 'data-no-auto-translation'; $translateable_strings = array(); $skip_machine_translating_strings = array(); $nodes = array(); $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $html = TranslatePress\str_get_html($output, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT); if ( $html === false ){ $trpremoved = $this->remove_trp_html_tags( $output ); return $trpremoved; } $count_translation_blocks = 0; if ( $translate_normal_strings ) { $all_existing_translation_blocks = $this->trp_query->get_all_translation_blocks( $language_code ); // trim every translation block original now, to avoid over-calling trim function later foreach ( $all_existing_translation_blocks as $key => $existing_tb ) { $all_existing_translation_blocks[ $key ]->trimmed_original = $this->trim_translation_block( $all_existing_translation_blocks[ $key ]->original ); } /* Try to find if there are any blocks in the output for translation. * If the output is an actual html page, use only the innertext of body tag * Else use the entire output (ex. the output is from JSON REST API content, or just a string) */ $html_body = $html->find('body', 0 ); $output_to_translate = ( $html_body ) ? $html_body->innertext : $output; $trimmed_html_body = $this->trim_translation_block( $output_to_translate ); foreach( $all_existing_translation_blocks as $key => $existing_translation_block ){ if ( (empty($existing_translation_block->trimmed_original )) || (strpos( $trimmed_html_body, $existing_translation_block->trimmed_original ) === false )){ unset($all_existing_translation_blocks[$key] );//if it isn't present remove it, this way we don't look for them on pages that don't contain blocks } } $count_translation_blocks = count( $all_existing_translation_blocks );//see here how many remain on the current page $merge_rules = $this->translation_manager->get_merge_rules(); } /** * When we are in the translation editor: Intercept the trp-gettext that was wrapped around all the gettext texts, grab the attribute data-trpgettextoriginal * which contains the original translation id and move it to the parent node if the parent node only contains that string then remove the wrap trp-gettext, otherwise replace it with another tag. * Also set a no-translation attribute. * When we are in a live translation case: Intercept the trp-gettext that was wrapped around all the gettext texts, set a no-translation attribute to the parent node if the parent node only contains that string * then remove the wrap trp-gettext, otherwise replace the wrap with another tag and do the same to it * We identified two cases: the wrapper trp-gettext can be as a node in the dome or ot can be inside a html attribute ( for example value ) * and we need to treat them differently */ /* store the nodes in arrays so we can sort the $trp_rows which contain trp-gettext nodes from the DOM according to the number of children and we process the simplest first */ $trp_rows = array(); $trp_attr_rows = array(); foreach ( $html->find("*[!nuartrebuisaexiteatributulasta]") as $k => $row ){ if( $row->hasAttribute('data-trpgettextoriginal') ){ $trp_rows[count( $row->children )][] = $row; } else{ if( $row->nodetype !== 5 && $row->nodetype !== 3 )//add all tags that are not root or text, text nodes can't have attributes $trp_attr_rows[] = $row; if ( $translate_normal_strings && $count_translation_blocks > 0 ) { $translation_block = $this->find_translation_block( $row, $all_existing_translation_blocks, $merge_rules ); if ( $translation_block ) { $existing_classes = $row->getAttribute( 'class' ); if ( $translation_block->block_type == 1 ) { $found_inner_translation_block = false; foreach ( $row->children() as $child ) { if ( $this->find_translation_block( $child, array( $translation_block ), $merge_rules ) != null ) { $found_inner_translation_block = true; break; } } if ( ! $found_inner_translation_block ) { // make sure we find it later exactly the way it is in DB $row->innertext = $translation_block->original; $row->setAttribute( 'class', $existing_classes . ' translation-block' ); } } else if ( $preview_mode && $translation_block->block_type == 2 && $translation_block->status != 0 ) { // refactor to not do this for each $row->setAttribute( 'data-trp-translate-id', $translation_block->id ); $row->setAttribute( 'data-trp-translate-id-deprecated', $translation_block->id ); $row->setAttribute( 'class', $existing_classes . 'trp-deprecated-tb' ); } } } } } /* sort them here ascending by key where the key is the number of children */ /* here we add support for gettext inside gettext */ ksort($trp_rows); foreach( $trp_rows as $level ){ foreach( $level as $row ){ $original_gettext_translation_id = $row->getAttribute('data-trpgettextoriginal'); /* Parent node has no other children and no other innertext besides the current node */ if( count( $row->parent()->children ) == 1 && $row->parent()->innertext == $row->outertext ){ $row->outertext = $row->innertext(); $row->parent()->setAttribute($no_translate_attribute, ''); $row->parent()->setAttribute('data-trp-gettext', ''); // we are in the editor if (isset($_REQUEST['trp-edit-translation']) && $_REQUEST['trp-edit-translation'] == 'preview') { //move up the data-trpgettextoriginal attribute $row->parent()->setAttribute('data-trpgettextoriginal', $original_gettext_translation_id); } } else{ /* Setting this attribute using setAttribute function actually changes the $html object. Important for not detecting this gettext as a regular string in the next lines using find() */ $row->setAttribute($no_translate_attribute, ''); /* Changes made to outertext take place only after saving the html object to a string */ $row->outertext = '<trp-wrap class="trp-wrap" data-no-translation'; if (isset($_REQUEST['trp-edit-translation']) && $_REQUEST['trp-edit-translation'] == 'preview') { $row->outertext .= ' data-trpgettextoriginal="'. $original_gettext_translation_id .'"'; } $row->outertext .= '>'.$row->innertext().'</trp-wrap>'; } } } foreach( $trp_attr_rows as $row ){ $all_attributes = $row->getAllAttributes(); if( !empty( $all_attributes ) ) { foreach ($all_attributes as $attr_name => $attr_value) { if (strpos($attr_value, 'trp-gettext ') !== false) { //if we have json content in the value of the attribute, we don't do anything. The trp-wrap will be removed later in the code if (is_array($json_array = json_decode( html_entity_decode( $attr_value, ENT_QUOTES ), true ) ) ) { continue; } // convert to a node $node_from_value = TranslatePress\str_get_html(html_entity_decode(htmlspecialchars_decode($attr_value, ENT_QUOTES)), true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT); if ( $node_from_value === false ){ continue; } foreach ($node_from_value->find('trp-gettext') as $nfv_row) { $nfv_row->outertext = $nfv_row->innertext(); $saved_node_from_value = $node_from_value->save(); // attributes of these tags are not handled well by the parser so don't escape them [see iss6264] if ( $row->tag != 'script' && $row->tag != 'style' ){ $saved_node_from_value = esc_attr($saved_node_from_value); } $row->setAttribute($attr_name, $saved_node_from_value ); $row->setAttribute($no_translate_attribute . '-' . $attr_name, ''); // we are in the editor if (isset($_REQUEST['trp-edit-translation']) && $_REQUEST['trp-edit-translation'] == 'preview') { $original_gettext_translation_id = $nfv_row->getAttribute('data-trpgettextoriginal'); $row->setAttribute('data-trpgettextoriginal-' . $attr_name, $original_gettext_translation_id); } } } } } } if ( ! $translate_normal_strings ) { /* save it as a string */ $trpremoved = $html->save(); /* perform preg replace on the remaining trp-gettext tags */ $trpremoved = $this->remove_trp_html_tags($trpremoved ); return $trpremoved; } $no_translate_selectors = apply_filters( 'trp_no_translate_selectors', array( '#wpadminbar' ), $TRP_LANGUAGE ); $ignore_cdata = apply_filters('trp_ignore_cdata', true ); $translate_encoded_html_as_string = apply_filters('trp_translate_encoded_html_as_string', false ); $translate_encoded_html_as_html = apply_filters('trp_translate_encoded_html_as_html', true ); // used for skipping minified scripts but can be used for anything $skip_strings_containing_key_terms = apply_filters('trp_skip_strings_containing_key_terms', array( array( 'terms'=> array( 'function', 'return', 'if', '==' ), 'operator' => 'and' ) ) ); /* * process the types of strings we can currently have: no-translate, translation-block, text, input, textarea, etc. */ foreach ( $no_translate_selectors as $no_translate_selector ){ foreach ( $html->find( $no_translate_selector ) as $k => $row ){ $row->setAttribute( $no_translate_attribute, '' ); } } $no_auto_translate_selectors = apply_filters( 'trp_no_auto_translate_selectors', array( ), $TRP_LANGUAGE ); foreach ( $no_auto_translate_selectors as $no_auto_translate_selector ){ foreach ( $html->find( $no_auto_translate_selector ) as $k => $row ){ $row->setAttribute( $no_auto_translate_attribute, '' ); } } foreach ( $html->find('.translation-block') as $row ){ $trimmed_string = trp_full_trim( $row->innertext ); $parent = $row->parent(); if( $trimmed_string!="" && $parent->tag!="script" && $parent->tag!="style" && $parent->tag != 'title' && strpos($row->outertext,'[vc_') === false && !$this->trp_is_numeric($trimmed_string) && !preg_match('/^\d+%$/',$trimmed_string) && $row->find_ancestor_tag( 'script' ) === null // sometimes the script has an html tree that gets detected, so script is not a direct parent && !$this->has_ancestor_attribute( $row, $no_translate_attribute ) ) { $string_count = array_push( $translateable_strings, $trimmed_string ); array_push( $nodes, array('node' => $row, 'type' => 'block')); //add data-trp-post-id attribute if needed $nodes = $this->maybe_add_post_id_in_node( $nodes, $row, $string_count ); } } foreach ( $html->find('trptext') as $row ){ $outertext = $row->outertext; $parent = $row->parent(); $trimmed_string = trp_full_trim( $outertext ); if( $trimmed_string!="" && $parent->tag!="script" && $parent->tag!="style" && $parent->tag != 'title' && $parent->tag != 'textarea' //explicitly exclude textarea strings && strpos($outertext,'[vc_') === false && !$this->trp_is_numeric($trimmed_string) && !preg_match('/^\d+%$/',$trimmed_string) && !$this->has_ancestor_attribute( $row, $no_translate_attribute ) && !$this->has_ancestor_class( $row, 'translation-block') && $row->find_ancestor_tag( 'script' ) === null // sometimes the script has an html tree that gets detected, so script is not a direct parent && ( !$ignore_cdata || ( strpos($trimmed_string, '<![CDATA[') !== 0 && strpos($trimmed_string, '<![CDATA[') !== 0 ) ) && (strpos($trimmed_string, 'BEGIN:VCALENDAR') !== 0) && !$this->contains_substrings($trimmed_string, $skip_strings_containing_key_terms ) ) { if ( !$translate_encoded_html_as_string ){ $is_html = false; if ( $translate_encoded_html_as_html ){ if ( $this->is_html($trimmed_string) ){ // prevent potential infinite loops. Only call translate_page once recursively add_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $row->outertext = str_replace( $trimmed_string, $this->translate_page( $trimmed_string ), $row->outertext ); remove_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $is_html = true; }else { $entity_decoded_trimmed_string = html_entity_decode( $trimmed_string ); if ( $this->is_html( $entity_decoded_trimmed_string ) ) { // prevent potential infinite loops. Only call translate_page once recursively add_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $row->outertext = str_replace( $trimmed_string, htmlentities( $this->translate_page( $entity_decoded_trimmed_string ) ), $row->outertext ); remove_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $is_html = true; } } } if ( $is_html ) { continue; } } // $translateable_strings array needs to be in sync in $nodes array $string_count = array_push( $translateable_strings, $trimmed_string ); $node_type_to_push = ( in_array( $parent->tag, array( 'button', 'option' ) ) ) ? $parent->tag : 'text'; array_push($nodes, array('node' => $row, 'type' => $node_type_to_push )); if ( ! apply_filters( 'trp_allow_machine_translation_for_string', true, $trimmed_string, null, null, $row ) ){ array_push( $skip_machine_translating_strings, $trimmed_string ); } //add data-trp-post-id attribute if needed $nodes = $this->maybe_add_post_id_in_node( $nodes, $row, $string_count ); } $row = apply_filters( 'trp_process_other_text_nodes', $row ); } //set up general links variables $home_url = home_url(); $node_accessors = $this->get_node_accessors(); foreach( $node_accessors as $node_accessor_key => $node_accessor ){ if ( isset( $node_accessor['selector'] ) ){ foreach ( $html->find( $node_accessor['selector'] ) as $k => $row ){ $current_node_accessor_selector = $node_accessor['accessor']; $trimmed_string = trp_full_trim( $row->$current_node_accessor_selector ); if ( $current_node_accessor_selector === 'href' ) { $translate_href = ( $this->is_external_link( $trimmed_string, $home_url ) || $this->url_converter->url_is_file( $trimmed_string ) || $this->url_converter->url_is_extra($trimmed_string) ); $translate_href = apply_filters( 'trp_translate_this_href', $translate_href, $row, $TRP_LANGUAGE ); $trimmed_string = ( $translate_href ) ? $trimmed_string : ''; } if( $trimmed_string!="" && !$this->trp_is_numeric($trimmed_string) && !preg_match('/^\d+%$/',$trimmed_string) && !$this->has_ancestor_attribute( $row, $no_translate_attribute ) && !$this->has_ancestor_attribute( $row, $no_translate_attribute . '-' . $current_node_accessor_selector ) && !$this->has_ancestor_class( $row, 'translation-block') && $row->tag != 'link' && ( !$ignore_cdata || ( strpos($trimmed_string, '<![CDATA[') !== 0 && strpos($trimmed_string, '<![CDATA[') !== 0 ) ) && (strpos($trimmed_string, 'BEGIN:VCALENDAR') !== 0 ) && !$this->contains_substrings($trimmed_string, $skip_strings_containing_key_terms ) ) { $entity_decoded_trimmed_string = html_entity_decode( $trimmed_string ); if ( !$translate_encoded_html_as_string ){ if ( $translate_encoded_html_as_html ){ if ( $this->is_html($entity_decoded_trimmed_string) ){ // prevent potential infinite loops. Only call translate_page once recursively add_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $row->setAttribute( $current_node_accessor_selector, str_replace( $trimmed_string, esc_attr( htmlentities($this->translate_page( $entity_decoded_trimmed_string )) ), $row->$current_node_accessor_selector ) ); remove_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); continue; } } } array_push( $translateable_strings, $entity_decoded_trimmed_string ); array_push( $nodes, array( 'node'=>$row, 'type' => $node_accessor_key ) ); if ( ! apply_filters( 'trp_allow_machine_translation_for_string', true, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor, $row ) ){ array_push( $skip_machine_translating_strings, $entity_decoded_trimmed_string ); } } } } } $translateable_information = array( 'translateable_strings' => $translateable_strings, 'nodes' => $nodes ); $translateable_information = apply_filters( 'trp_translateable_strings', $translateable_information, $html, $no_translate_attribute, $TRP_LANGUAGE, $language_code, $this ); $translateable_strings = $translateable_information['translateable_strings']; $nodes = $translateable_information['nodes']; $translated_strings = $this->process_strings( $translateable_strings, $language_code, null, $skip_machine_translating_strings ); do_action('trp_translateable_information', $translateable_information, $translated_strings, $language_code); //check for post_id meta on original strings, and insert for non existing /* * - get only strings that have the post_id in the nodes from $translateable_information * - get the original id's for these string from the original table * - see which of these id's have the meta with the current post_id value and insert into the meta table the ones that don't * */ if( !empty($translateable_information['nodes']) ){ $strings_in_post_content = array(); foreach ( $translateable_information['nodes'] as $i => $node ){ if( !empty( $node['post_id'] ) ){ $strings_in_post_content['strings'][] = $translateable_information['translateable_strings'][$i]; $strings_in_post_content['post_ids'][] = $node['post_id']; } } if( !empty( $strings_in_post_content ) ){ //try to do this only once a day to decrease query load $current_permalink = get_permalink(); $set_meta_for_this_url = get_transient('processed_original_string_meta_post_id_for_' . hash('md4', $current_permalink)); if( $set_meta_for_this_url === false ){ $original_string_ids = $this->trp_query->get_original_string_ids($strings_in_post_content['strings']); if( !empty( $original_string_ids ) ){ //there is a correlation between the two arrays $this->trp_query->set_original_string_meta_post_id( $original_string_ids, $strings_in_post_content['post_ids'] ); } set_transient('processed_original_string_meta_post_id_for_' . hash('md4', $current_permalink), 'done', 60*60*24 ); } } } if ( $preview_mode ) { $translated_string_ids = $this->trp_query->get_string_ids($translateable_strings, $language_code); } foreach ( $nodes as $i => $node ) { $translation_available = isset( $translated_strings[$i] ); if ( ! ( $translation_available || $preview_mode ) || !isset( $node_accessors [$nodes[$i]['type']] )){ continue; } $current_node_accessor = $node_accessors[ $nodes[$i]['type'] ]; $accessor = $current_node_accessor[ 'accessor' ]; if ( $translation_available && isset( $current_node_accessor ) && ! ( $preview_mode && ( $this->settings['default-language'] == $TRP_LANGUAGE ) ) ) { $translateable_string = $translateable_strings[$i]; if ( $current_node_accessor[ 'attribute' ] ){ $translateable_string = $this->maybe_correct_translatable_string( $translateable_string, $nodes[$i]['node']->getAttribute( $accessor ) ); $nodes[$i]['node']->setAttribute( $accessor, str_replace( $translateable_string, esc_attr( $translated_strings[$i] ), $nodes[$i]['node']->getAttribute( $accessor ) ) ); do_action( 'trp_set_translation_for_attribute', $nodes[$i]['node'], $accessor, $translated_strings[$i] ); }else{ $translateable_string = $this->maybe_correct_translatable_string( $translateable_string, $nodes[$i]['node']->$accessor ); $nodes[$i]['node']->$accessor = str_replace( $translateable_string, trp_sanitize_string($translated_strings[$i]), $nodes[$i]['node']->$accessor ); } } if ( $preview_mode ) { if ( $accessor == 'outertext' && $nodes[$i]['type'] != 'button' ) { $outertext_details = '<translate-press data-trp-translate-id="' . $translated_string_ids[$translateable_strings[$i]]->id . '" data-trp-node-group="' . $this->get_node_type_category( $nodes[$i]['type'] ) . '"'; if ( $this->get_node_description( $nodes[$i] ) ) { $outertext_details .= ' data-trp-node-description="' . $this->get_node_description($nodes[$i] ) . '"'; } $outertext_details .= '>' . $nodes[$i]['node']->outertext . '</translate-press>'; $nodes[$i]['node']->outertext = $outertext_details; } else { if( $nodes[$i]['type'] == 'button' || $nodes[$i]['type'] == 'option' ){ $nodes[$i]['node'] = $nodes[$i]['node']->parent(); } $nodes[$i]['node']->setAttribute('data-trp-translate-id-' . $accessor, $translated_string_ids[ $translateable_strings[$i] ]->id ); $nodes[$i]['node']->setAttribute('data-trp-node-group-' . $accessor, $this->get_node_type_category( $nodes[$i]['type'] ) ); if ( $this->get_node_description( $nodes[$i] ) ) { $nodes[$i]['node']->setAttribute('data-trp-node-description-' . $accessor, $this->get_node_description($nodes[$i])); } } } } // We need to save here in order to access the translated links too. if( apply_filters('tp_handle_custom_links_in_translation_blocks', false) ) { $html_string = $html->save(); $html = TranslatePress\str_get_html($html_string, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT); if ( $html === false ){ return $html_string; } } $html = $this->handle_custom_links_and_forms( $html ); // Append an html table containing the errors $trp_editor_notices = apply_filters( 'trp_editor_notices', $trp_editor_notices ); if ( trp_is_translation_editor('preview') && $trp_editor_notices != '' ){ $body = $html->find('body', 0 ); if ( $body ) { $body->innertext = '<div data-no-translation class="trp-editor-notices">' . $trp_editor_notices . "</div>" . $body->innertext; } } $final_html = $html->save(); /* perform preg replace on the remaining trp-gettext tags */ $final_html = $this->remove_trp_html_tags( $final_html ); return apply_filters( 'trp_translated_html', $final_html, $TRP_LANGUAGE, $language_code, $preview_mode ); } public function handle_custom_links_and_forms( $html ){ global $TRP_LANGUAGE; $preview_mode = isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview'; $home_url = home_url(); $admin_url = admin_url(); $wp_login_url = wp_login_url(); $no_translate_attribute = 'data-no-translation'; if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } // force custom links to have the correct language foreach( $html->find('a[href!="#"]') as $a_href) { $a_href->href = apply_filters( 'trp_href_from_translated_page', $a_href->href, $this->settings['default-language'] ); $url = trim($a_href->href); $url = $this->maybe_is_local_url($url, $home_url); $is_external_link = $this->is_external_link( $url, $home_url ); $is_admin_link = $this->is_admin_link($url, $admin_url, $wp_login_url); if( $preview_mode && ! $is_external_link ){ $a_href->setAttribute( 'data-trp-original-href', $url ); } if ( ( $TRP_LANGUAGE != $this->settings['default-language'] || $this->settings['add-subdirectory-to-default-language'] == 'yes' ) && $this->settings['force-language-to-custom-links'] == 'yes' && !$is_external_link && !$this->url_converter->url_is_file( $url ) && ( $this->url_converter->get_lang_from_url_string( $url ) == null || ( isset ($this->settings['add-subdirectory-to-default-language']) && $this->settings['add-subdirectory-to-default-language'] === 'yes' && $this->url_converter->get_lang_from_url_string( $url ) === $this->settings['default-language'] ) ) && !$is_admin_link && strpos($url, '#TRPLINKPROCESSED') === false && ( !$this->has_ancestor_attribute( $a_href, $no_translate_attribute ) || $this->has_ancestor_attribute($a_href, 'data-trp-gettext') ) // add language param to link if it's inside a gettext ){ $a_href->href = apply_filters( 'trp_force_custom_links', $this->url_converter->get_url_for_language( $TRP_LANGUAGE, $url, '' ), $url, $TRP_LANGUAGE, $a_href ); $url = $a_href->href; } if( $preview_mode && ( $is_external_link || $this->is_different_language( $url ) || $is_admin_link ) ) { $a_href->setAttribute( 'data-trp-unpreviewable', 'trp-unpreviewable' ); } $a_href->href = str_replace('#TRPLINKPROCESSED', '', $a_href->href); } // pass the current language in forms where the action does not contain the language // based on this we're filtering wp_redirect to include the proper URL when returning to the current page. foreach ( $html->find('form') as $k => $row ){ $form_action = $row->action; $is_admin_link = $this->is_admin_link( $form_action, $admin_url, $wp_login_url ); if(!$is_admin_link) { $row->setAttribute( 'data-trp-original-action', $row->action ); $row->innertext .= apply_filters( 'trp_form_inputs', '<input type="hidden" name="trp-form-language" value="' . $this->settings['url-slugs'][ $TRP_LANGUAGE ] . '"/>', $TRP_LANGUAGE, $this->settings['url-slugs'][ $TRP_LANGUAGE ], $row ); $is_external_link = $this->is_external_link( $form_action, $home_url ); if ( !empty( $form_action ) && $this->settings['force-language-to-custom-links'] == 'yes' && !$is_external_link && strpos( $form_action, '#TRPLINKPROCESSED' ) === false ) { $row->action = $this->url_converter->get_url_for_language( $TRP_LANGUAGE, $form_action ); } $row->action = str_replace( '#TRPLINKPROCESSED', '', esc_url($row->action) ); } } foreach ( $html->find('link') as $link ) { if ( isset($link->href) ) { $link->href = str_replace('#TRPLINKPROCESSED', '', $link->href); } } return $html; } public function is_first_language_not_default_language(){ return ( isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] == 'yes' && isset( $this->settings['publish-languages'][0] ) && $this->settings['default-language'] != $this->settings['publish-languages'][0] ); } /* * Adjust translatable string so that it must match the content of the node value * * We use str_replace method in order to preserve any existent spacing before or after the string. * If the encoding of the node is not the same as the translatable string then the string won't match so try applying htmlentities. * If that doesn't work either, just forget about any possible before and after spaces. * */ public function maybe_correct_translatable_string( $translatable_string, $node_value ){ if ( strpos ( $node_value, $translatable_string ) === false ){ $translatable_string = htmlentities( $translatable_string ); if ( strpos ( $node_value, $translatable_string ) === false ){ $translatable_string = $node_value; } } return $translatable_string; } public function maybe_add_post_id_in_node( $nodes, $row, $string_count ){ $post_container_node = $this->has_ancestor_attribute( $row, 'data-trp-post-id' ); if( $post_container_node && $post_container_node->attr['data-trp-post-id'] ) { $nodes[$string_count - 1]['post_id'] = $post_container_node->attr['data-trp-post-id']; } return $nodes; } /* * Update other image attributes (srcset) with the translated image * * Hooked to trp_set_translation_for_attribute */ public function translate_image_srcset_attributes( $node, $accessor, $translated_string){ if( $accessor === 'src' ) { $srcset = $node->getAttribute( 'srcset' ); $datasrcset = $node->getAttribute( 'data-srcset' ); if ( $srcset || $datasrcset ) { $attachment_id = attachment_url_to_postid( $translated_string ); if ( $attachment_id ) { $translated_srcset = ''; if ( function_exists( 'wp_get_attachment_image_srcset' ) ) { // get width of the image in order, to set the largest possible size for srcset $meta_data = wp_get_attachment_metadata( $attachment_id ); $width = ( $meta_data && isset( $meta_data['width'] ) ) ? $meta_data['width'] : 'large'; $translated_srcset = wp_get_attachment_image_srcset( $attachment_id, $width ); } if ( $srcset ){ $node->setAttribute( 'srcset', $translated_srcset ); } if ( $datasrcset ){ $node->setAttribute( 'data-srcset', $translated_srcset ); } } else { $node->setAttribute( 'srcset', '' ); $node->setAttribute( 'data-srcset', '' ); } } if ( $node->getAttribute( 'data-src' ) ) { $node->setAttribute( 'data-src', $translated_string ); } } } /* * Do not automatically translate src and href attributes * * Hooked to trp_allow_machine_translation_for_string */ public function allow_machine_translation_for_string( $allow, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor ){ $skip_attributes = apply_filters( 'trp_skip_machine_translation_for_attr', array( 'href', 'src' ) ); if ( in_array( $current_node_accessor_selector, $skip_attributes ) ){ // do not machine translate href and src return false; } return $allow; } /* * Do not automatically translate html nodes with data-no-auto-translation attribute * * Hooked to trp_allow_machine_translation_for_string */ function skip_automatic_translation_for_no_auto_translation_selector($allow, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor, $row){ $no_auto_translate_attribute = 'data-no-auto-translation'; if ( $this->has_ancestor_attribute( $row, $no_auto_translate_attribute ) || ( $current_node_accessor_selector !== null && $this->has_ancestor_attribute( $row, $no_auto_translate_attribute . '-' . $current_node_accessor_selector )) ){ return false; } return $allow; } /** * function that removes any unwanted leftover <trp-gettext> tags * @param $string * @return string|string[]|null */ function remove_trp_html_tags( $string ){ $string = preg_replace( '/(<|<)trp-gettext (.*?)(>|>)/i', '', $string ); $string = preg_replace( '/(<|<)(\\\\)*\/trp-gettext(>|>)/i', '', $string ); // In case we have a gettext string which was run through rawurlencode(). See more details on iss6563 $string = preg_replace( '/%23%21trpst%23trp-gettext(.*?)%23%21trpen%23/i', '', $string ); $string = preg_replace( '/%23%21trpst%23%2Ftrp-gettext%23%21trpen%23/i', '', $string ); if (!isset($_REQUEST['trp-edit-translation']) || $_REQUEST['trp-edit-translation'] != 'preview') { $string = preg_replace('/(<|<)trp-wrap (.*?)(>|>)/i', '', $string); $string = preg_replace('/(<|<)(\\\\)*\/trp-wrap(>|>)/i', '', $string); } //remove post containers before outputting $string = preg_replace( '/(<|<)trp-post-container (.*?)(>|>)/i', '', $string ); $string = preg_replace( '/(<|<)(\\\\)*\/trp-post-container(>|>)/i', '', $string ); return $string; } /** * Callback for the array_walk_recursive to translate json. It translates the values in the resulting json array if they contain html * @param $value */ function translate_json (&$value) { //check if it a html text and translate $html_decoded_value = html_entity_decode( (string) $value ); if ( $html_decoded_value != strip_tags( $html_decoded_value ) ) { $value = $this->translate_page( $value ); /*the translate-press tag can appear on a gettext string without html and should not be left in the json as we don't know how it will be inserted into the page by js */ $value = preg_replace( '/(<|<)translate-press (.*?)(>|>)/', '', $value ); $value = preg_replace( '/(<|<)(\\\\)*\/translate-press(>|>)/', '', $value ); } } /** * Callback for the array_walk_recursive to process links inside json elements that might contain HTML. It processes the values in the resulting json array if they contain html * @param $value */ function custom_links_and_forms_json (&$value) { //check if it a html text and translate $html_decoded_value = html_entity_decode( (string) $value ); if ( $html_decoded_value != strip_tags( $html_decoded_value ) ) { $html = TranslatePress\str_get_html( $value, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if( $html ) { $html = $this->handle_custom_links_and_forms($html); $value = $html->save(); } } } public function handle_custom_links_for_default_language(){ return ( $this->settings['force-language-to-custom-links'] == 'yes' && $this->is_first_language_not_default_language() && apply_filters('trp_handle_custom_links_and_forms_in_default_language', true ) ); } /** * Function that should be called only on the default language and when we are not in the editor mode and it is designed as a fallback to clear * any trp gettext tags that we added and for some reason show up although they should not * @param $output * @return mixed */ public function render_default_language( $output ){ if ( $this->handle_custom_links_for_default_language() && !apply_filters( 'trp_stop_translating_page', false, $output ) ) { $json_array = json_decode( $output, true ); /* If we have a json response we need to parse it and only translate the nodes that contain html * * Removed is_ajax_on_frontend() check because we need to capture custom ajax events. * Decided that if $output is json decodable it's a good enough check to handle it this way. * We have necessary checks so that we don't get to this point when is_admin(), or when language is not default. */ if( $json_array && $json_array != $output ) { /* if it's one of our own ajax calls don't do nothing */ if ( ! empty( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 && $_REQUEST['action'] != 'trp_split_translation_block' ) { return $output; } //check if we have a json response if ( ! empty( $json_array ) ) { if( is_array( $json_array ) ) { array_walk_recursive($json_array, array($this, 'custom_links_and_forms_json')); } else { $html = TranslatePress\str_get_html( $json_array, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if( $html ) { $html = $this->handle_custom_links_and_forms($html); $json_array = $html->save(); } } } return trp_safe_json_encode( $json_array ); } $html = TranslatePress\str_get_html( $output, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if( $html ) { $html = $this->handle_custom_links_and_forms($html); $output = $html->save(); } } return TRP_Translation_Manager::strip_gettext_tags($output); } /** * Whether given url links to an external domain. * * @param string $url Url. * @param string $home_url Optional home_url so we avoid calling the home_url() inside loops. * @return bool Whether given url links to an external domain. */ public function is_external_link( $url, $home_url = '' ){ // Abort if parameter URL is empty if( empty($url) ) { return false; } if ( strpos( $url, '#' ) === 0 || strpos( $url, '/' ) === 0){ return false; } // Parse home URL and parameter URL $link_url = parse_url( $url ); if( empty( $home_url ) ) $home_url = home_url(); $home_url = parse_url( $home_url ); // Decide on target if( !isset ($link_url['host'] ) || $link_url['host'] == $home_url['host'] ) { // Is an internal link return false; } else { // Is an external link return true; } } /** * Checks to see if the user didn't incorrectly formated a url that's different from the home_url * Takes into account http, https, www and all the possible combinations between them. * * @param string $url Url. * @param string $home_url Optional home_url so we avoid calling the home_url() inside loops. * @return string Correct URL that's the same structure as home_url */ public function maybe_is_local_url( $url, $home_url='' ){ if ( apply_filters('disable_maybe_is_local_url', false) ){ return $url; } // Abort if parameter URL is empty if( empty($url) ) { return $url; } if ( strpos( $url, '#' ) === 0 || strpos( $url, '/' ) === 0){ return $url; } // Parse home URL and parameter URL $link_url = parse_url( $url ); if( empty( $home_url ) ) $home_url = home_url(); $home_url = parse_url( $home_url ); // Decide on target if( !isset ($link_url['host'] ) || $link_url['host'] == $home_url['host'] || !isset ( $link_url['scheme'] ) ) { // Is an internal link return $url; } else { // test out possible local urls that the user might have mistyped $valid_local_prefix = array('http://', 'https://', 'http://www.', 'https://www.'); foreach ($valid_local_prefix as $prefix){ foreach ($valid_local_prefix as $replacement_prefix){ if( str_replace($prefix, $replacement_prefix, $link_url['scheme'] . '://' . $link_url['host']) == $home_url['scheme'] . '://' .$home_url['host'] ){ return str_replace($prefix, $replacement_prefix, $url); } } } // Is an external link return $url; } } /** * Whether given url links to a different language than the current one. * * @param string $url Url. * @return bool Whether given url links to a different language than the current one. */ protected function is_different_language( $url ){ global $TRP_LANGUAGE; if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $lang = $this->url_converter->get_lang_from_url_string( $url ); if ( $lang == null ){ $lang = $this->settings['default-language']; } if ( $lang == $TRP_LANGUAGE ){ return false; }else{ return true; } } /** * Whether given url links to an admin page. * * @param string $url Url. * @return bool Whether given url links to an admin page. */ protected function is_admin_link( $url, $admin_url = '', $wp_login_url = '' ){ if( empty( $admin_url ) ) $admin_url = admin_url(); if( empty( $wp_login_url ) ) $wp_login_url = wp_login_url(); if ( strpos( $url, $admin_url ) !== false || strpos( $url, $wp_login_url ) !== false ){ $is_admin_link = true; } else { $is_admin_link = false; } return apply_filters('trp_is_admin_link', $is_admin_link, $url, $admin_url, $wp_login_url); } /** * Return translations for given strings in given language code. * * Also stores new strings, calls automatic translations and stores new translations. * * @param $translateable_strings * @param $language_code * @return array */ public function process_strings( $translateable_strings, $language_code, $block_type = null, $skip_machine_translating_strings = array() ) { if ( !in_array( $language_code, $this->settings['translation-languages'] ) || $language_code === $this->settings['default-language'] ) { return array(); } if ( !$this->machine_translator ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->machine_translator = $trp->get_component( 'machine_translator' ); } $translated_strings = array(); $machine_translation_available = $this->machine_translator ? $this->machine_translator->is_available( array( $this->settings['default-language'], $language_code ) ) : false; $originals_without_translation_in_db_that_are_similar_with_already_translated_strings = array(); if ( !$this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } // get existing translations $dictionary = $this->trp_query->get_existing_translations( array_values( $translateable_strings ), $language_code ); if ( $dictionary === false ) { return array(); } $new_strings = array(); $machine_translatable_strings = array(); /** * The filter 'trp_add_similar_and_original_strings_to_db' becomes true only when TranslatePress Settings->Advanced Settings->Serve similar translations for strings that are almost identical * is set to 'Yes' * This filter appears in multiple places in this class since we use the function process_strings to make sure all the strings are inserted in the DataBase with their corresponding translation * The similar strings we reference are strings that are almost identical with strings that are in the DB and have translations. * We use those translated strings to translate the similar strings so the user or the translation engine does not have to do it anymore and we can * display them in frontend (for more information about how this process is done please look at the file tranlatepress\includes\advanced-settings\serve-similar-translation.php) * * In the code below we are populating the array '$originals_without_translation_in_db_that_are_similar_with_already_translated_strings' with original strings * from the page that do not have a translation, but are almost identical with strings that are already in DB with translation. * A similar string is determined by looking at its value in $dictionary (an array that contains all the strings on the page). The $dictionary has as keys * the strings mentioned before, which are objects containing more arguments, one of them being original. However, in the case of a similar original string * without translation, the argument 'original' is the almost identical string that has a translation stored in the DB. * * We collect these similar strings in an array so we can merge them later in new_strings in order to be introduced into the DB. */ if ( apply_filters( 'trp_add_similar_and_original_strings_to_db', false ) ) { foreach ( array_keys( $dictionary ) as $value ) { if ( $value != $dictionary[ $value ]->original ) { $originals_without_translation_in_db_that_are_similar_with_already_translated_strings[] = $value; } } } foreach ( $translateable_strings as $i => $string ) { // prevent accidentally machine translated strings from db such as for src to be displayed $skip_string = in_array( $string, $skip_machine_translating_strings ); if ( isset( $dictionary[ $string ]->translated ) && $dictionary[ $string ]->status == $this->trp_query->get_constant_machine_translated() && $skip_string ) { continue; } //strings existing in database, if ( isset( $dictionary[ $string ]->translated ) ) { $translated_strings[ $i ] = $dictionary[ $string ]->translated; } else { $new_strings[ $i ] = $translateable_strings[ $i ]; // if the string is not a url then allow machine translation for it if ( $machine_translation_available && !$skip_string && filter_var( $new_strings[ $i ], FILTER_VALIDATE_URL ) === false ) { $machine_translatable_strings[ $i ] = $new_strings[ $i ]; } } /** * Here we look through the $translateable_strings found and if they are also in the array $originals_without_translation_in_db_that_are_similar_with_already_translated_strings * then we assign them to new_strings to prepare them for being inserted into DB. */ if ( apply_filters( 'trp_add_similar_and_original_strings_to_db', false ) ) { if ( in_array( $translateable_strings[ $i ], $originals_without_translation_in_db_that_are_similar_with_already_translated_strings ) ) { $new_strings[ $i ] = $translateable_strings[ $i ]; } } } $untranslated_list = $this->trp_query->get_untranslated_strings( $new_strings, $language_code ); $update_strings = array(); $unique_original_strings_with_machine_translations = array(); // machine translate new strings if ( $machine_translation_available ) { $machine_strings = $this->machine_translator->translate( $machine_translatable_strings, $language_code, $this->settings['default-language'] ); $unique_original_strings_with_machine_translations = array_keys( $machine_strings ); } /** * If the option is activated,we use the below code so the variable $original_to_be_synced contains the array of strings that are to be sent to a machine translation * engine together with the similar one, so they have entries into the table the trp_original_strings */ $originals_to_be_synced= array_merge( $unique_original_strings_with_machine_translations, $originals_without_translation_in_db_that_are_similar_with_already_translated_strings ); $original_inserts = $this->trp_query->original_strings_sync( $language_code, $originals_to_be_synced ); if ( $machine_translation_available ) { // insert unique machine translations into db. Only for strings newly discovered foreach ( $unique_original_strings_with_machine_translations as $string ) { $id = ( isset( $untranslated_list[ $string ] ) ) ? $untranslated_list[ $string ]->id : NULL; array_push( $update_strings, array( 'id' => $id, 'original_id' => $original_inserts[ $string ]->id, 'original' => trp_sanitize_string( $string, false ), 'translated' => trp_sanitize_string( $machine_strings[ $string ] ), 'status' => $this->trp_query->get_constant_machine_translated() ) ); } }else{ $machine_strings = false; } // update existing strings without translation if we have one now. also, do not insert duplicates for existing untranslated strings in db foreach( $new_strings as $i => $string ){ if ( !isset($translated_strings[$i]) && isset( $machine_strings[$string] ) ) { $translated_strings[$i] = $machine_strings[$string]; } /** * In this code we add the original similar strings, that have now more arguments, including the translation taken from the similar string in * DB, to the $update_strings array, following now to update the field in the DB with all the information gathered. * * We check if the new_string is not already in the DB and if it is we unset it in order to avoid multiple entries in DB for thr same string. * The similar strings have status 3 in DB. */ if ( apply_filters('trp_add_similar_and_original_strings_to_db', false) ) { if (isset($new_strings[$i]) && in_array($new_strings[$i],$originals_without_translation_in_db_that_are_similar_with_already_translated_strings ) ) { $id = ( isset( $untranslated_list[$string] ) ) ? $untranslated_list[$string]->id : NULL; array_push( $update_strings, array( 'id' => $id, 'original' => $new_strings[$i], 'translated' => trp_sanitize_string( $dictionary[$string]->translated ), 'status' => $this->trp_query->get_constant_similar_translated(), 'original_id' => $original_inserts[ $string ]->id) ); unset($new_strings[$i]); } } if ( isset( $untranslated_list[ $string ] ) || isset( $machine_strings[ $string ] ) ) { unset( $new_strings[ $i ] ); } } $this->trp_query->insert_strings( $new_strings, $language_code, $block_type ); $this->trp_query->update_strings( $update_strings, $language_code, array( 'id','original', 'translated', 'status', 'original_id' ) ); return $translated_strings; } /** * Whether given node has ancestor with given attribute. * * @param object $node Html Node. * @param string $attribute Attribute to search for. * @return mixed Whether given node has ancestor with given attribute. */ public function has_ancestor_attribute($node,$attribute) { $currentNode = $node; if ( isset( $node->$attribute ) ){ return $node; } while($currentNode->parent() && $currentNode->parent()->tag!="html") { if(isset($currentNode->parent()->$attribute)) return $currentNode->parent(); else $currentNode = $currentNode->parent(); } return false; } /** * Whether given node has ancestor with given class. * * @param object $node Html Node. * @param string $class class to search for * @return bool Whether given node has ancestor with given class. */ public function has_ancestor_class($node, $class) { $currentNode = $node; while($currentNode->parent() && $currentNode->parent()->tag!="html") { if(isset($currentNode->parent()->class) && strpos($currentNode->parent()->class, $class) !== false) { return true; } else { $currentNode = $currentNode->parent(); } } return false; } /** * Which attributes to translate and how to access them * * Nodes with "selector" attribute are automatically searched for in PHP translate_page and in JS translate-dom-changes * * @return array */ public function get_node_accessors(){ return apply_filters( 'trp_node_accessors', array( 'text' => array( 'accessor' => 'outertext', 'attribute' => false ), 'block' => array( 'accessor' => 'innertext', 'attribute' => false ), 'image_src' => array( 'selector' => 'img[src]', 'accessor' => 'src', 'attribute' => true ), 'submit' => array( 'selector' => 'input[type=\'submit\'],input[type=\'button\'], input[type=\'reset\']', 'accessor' => 'value', 'attribute' => true ), 'placeholder' => array( 'selector' => 'input[placeholder],textarea[placeholder]', 'accessor' => 'placeholder', 'attribute' => true ), 'title' => array( 'selector' => '[title]', 'accessor' => 'title', 'attribute' => true ), 'a_href' => array( 'selector' => 'a[href]', 'accessor' => 'href', 'attribute' => true ), 'button' => array( 'accessor' => 'outertext', 'attribute' => false ), 'option' => array( 'accessor' => 'innertext', 'attribute' => false ), 'aria_label' => array( 'selector' => '[aria-label]', 'accessor' => 'aria-label', 'attribute' => true ) )); } public function get_accessors_array( $prefix = '' ){ $accessor_array = array(); $node_accessors_array = $this->get_node_accessors(); foreach ( $node_accessors_array as $node_accessor ){ if ( isset ( $node_accessor['accessor'] ) ){ $accessor_array[] = $prefix . $node_accessor['accessor']; } } return array_values( array_unique( $accessor_array ) ); } /* * Enqueue scripts on all pages */ public function enqueue_scripts() { // so far only when woocommerce is active we need to enqueue this script on all pages if ( class_exists( 'WooCommerce' ) ){ wp_enqueue_script('trp-frontend-compatibility', TRP_PLUGIN_URL . 'assets/js/trp-frontend-compatibility.js', array(), TRP_PLUGIN_VERSION ); } } public function get_trp_data(){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $nonces = $this->translation_manager->editor_nonces(); $language_to_query = $TRP_LANGUAGE; if ( $TRP_LANGUAGE == $this->settings['default-language'] ) { foreach ($this->settings['translation-languages'] as $language) { if ( $language != $this->settings['default-language'] ) { $language_to_query = $language; break; } } } $language_to_query = ( count ( $this->settings['translation-languages'] ) < 2 ) ? '' : $language_to_query; return array( 'trp_custom_ajax_url' => apply_filters( 'trp_custom_ajax_url', TRP_PLUGIN_URL . 'includes/trp-ajax.php' ), 'trp_wp_ajax_url' => apply_filters( 'trp_wp_ajax_url', admin_url( 'admin-ajax.php' ) ), 'trp_language_to_query' => $language_to_query, 'trp_original_language' => $this->settings['default-language'], 'trp_current_language' => $TRP_LANGUAGE, 'trp_skip_selectors' => apply_filters( 'trp_skip_selectors_from_dynamic_translation', array( '[data-no-translation]', '[data-no-dynamic-translation]', '[data-trp-translate-id-innertext]', 'script', 'style', 'head', 'trp-span', 'translate-press' ), $TRP_LANGUAGE, $this->settings ), // data-trp-translate-id-innertext refers to translation block and it shouldn't be detected 'trp_base_selectors' => $this->get_base_attribute_selectors(), 'trp_attributes_selectors' => $this->get_node_accessors(), 'trp_attributes_accessors' => $this->get_accessors_array(), 'gettranslationsnonceregular' => $nonces['gettranslationsnonceregular'], 'showdynamiccontentbeforetranslation' => apply_filters( 'trp_show_dynamic_content_before_translation', false ), 'skip_strings_from_dynamic_translation' => apply_filters( 'trp_skip_strings_from_dynamic_translation', array() ), 'skip_strings_from_dynamic_translation_for_substrings' => apply_filters( 'trp_skip_strings_from_dynamic_translation_for_substrings', array( 'href' => array('amazon-adsystem', 'googleads', 'g.doubleclick') ) ), 'duplicate_detections_allowed' => apply_filters( 'trp_duplicate_detections_allowed', 100 ), 'trp_translate_numerals_opt' => isset ($this->settings["trp_advanced_settings"]["enable_numerals_translation"]) ? $this->settings["trp_advanced_settings"]["enable_numerals_translation"] : 'no', 'trp_no_auto_translation_selectors' => apply_filters( 'trp_no_auto_translate_selectors', array( '[data-no-auto-translation]' ), $TRP_LANGUAGE ) ); } /** * Enqueue dynamic translation script. */ public function enqueue_dynamic_translation(){ $enable_dynamic_translation = apply_filters( 'trp_enable_dynamic_translation', true ); if ( ! $enable_dynamic_translation ){ return; } global $TRP_LANGUAGE; if ( $TRP_LANGUAGE != $this->settings['default-language'] || ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ) ) { $this->output_dynamic_translation_script(); } } /** * If is_late_dom_html_plugin_active() returns true, echo script on shutdown hook priority 10 * * Otherwise, enqueue script * * @see is_late_dom_html_plugin_active() * */ public function output_dynamic_translation_script(){ $script_src = TRP_PLUGIN_URL . 'assets/js/trp-translate-dom-changes.js'; $trp_data = $this->get_trp_data(); $trp_plugin_ver = TRP_PLUGIN_VERSION; $echo_scripts = function() use ( $script_src, $trp_data, $trp_plugin_ver ){ echo '<script type="text/javascript" id="trp-dynamic-translator-js-extra"> var trp_data = ' . json_encode( $trp_data ) . ';</script>'; echo '<script src="' . esc_url( $script_src ) . '?ver=' . esc_attr($trp_plugin_ver) .'" id="trp-dynamic-translator-js"></script>'; }; if ( is_late_dom_html_plugin_active() ){ add_action( 'shutdown', $echo_scripts ); return; } wp_enqueue_script('trp-dynamic-translator', $script_src, array('jquery'), TRP_PLUGIN_VERSION, true ); wp_localize_script('trp-dynamic-translator', 'trp_data', $trp_data ); } /** * Skip base selectors (data-trp-translate-id, data-trpgettextoriginal etc.) * * The base selectors (without any suffixes) are placed only if their children do not contain any nodes that are translatable * * hooked to trp_skip_selectors_from_dynamic_translation * * @param $skip_selectors * * @return array */ public function skip_base_attributes_from_dynamic_translation( $skip_selectors ){ $base_attributes = $this->get_base_attribute_selectors(); $selectors_to_skip = array(); foreach( $base_attributes as $base_attribute ){ $selectors_to_skip[] = '[' . $base_attribute . ']'; } return array_merge( $skip_selectors, $selectors_to_skip ); } /* * Get base attribute selectors */ public function get_base_attribute_selectors(){ return apply_filters( 'trp_base_attribute_selectors', array( 'data-trp-translate-id', 'data-trpgettextoriginal', 'data-trp-post-slug' ) ); } /** * Add a filter on the wp_mail function so we allow shortcode usage and run it through our translate function so it cleans it up nice and maybe even replace some strings * @param $args * @return array */ public function wp_mail_filter( $args ){ if (!is_array($args)){ return $args; } if(array_key_exists('subject', $args)){ $args['subject'] = $this->translate_page( do_shortcode( $args['subject'] ) ); } if(array_key_exists('message', $args)){ $args['message'] = $this->translate_page( do_shortcode( $args['message'] ) ); } return $args; } /** * Filters the location redirect to add the preview parameter to the next page * @param $location * @param $status * @return string * @since 1.0.8 */ public function force_preview_on_url_redirect( $location, $status ){ if( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ){ $location = add_query_arg( 'trp-edit-translation', 'preview', $location ); } return $location; } /** * Filters the location redirect to add the current language based on the trp-form-language parameter * @param $location * @param $status * @return string * @since 1.1.2 */ public function force_language_on_form_url_redirect( $location, $status ){ if( isset( $_REQUEST[ 'trp-form-language' ] ) && !empty($_REQUEST[ 'trp-form-language' ]) ){ $form_language_slug = sanitize_text_field($_REQUEST[ 'trp-form-language' ]); $form_language = array_search($form_language_slug, $this->settings['url-slugs']); if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $location = $this->url_converter->get_url_for_language( $form_language, $location ); } return $location; } /** * Filters the output buffer of ajax calls that return json and adds the preview arg to urls * @param $output * @return string * @since 1.0.8 */ public function force_preview_on_url_in_ajax( $output ){ if ( TRP_Gettext_Manager::is_ajax_on_frontend() && isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] === 'preview' && $output != false ) { $result = json_decode($output, TRUE); if ( json_last_error() === JSON_ERROR_NONE) { if( !is_array( $result ) )//make sure we send an array as json_decode even with true parameter might not return one $result = array($result); array_walk_recursive($result, array($this, 'callback_add_preview_arg')); $output = trp_safe_json_encode($result); } //endif } //endif return $output; } /** * Adds preview query arg to links that are url's. callback specifically for the array_walk_recursive function * @param $item * @param $key * @return string * @internal param $output * @since 1.0.8 */ function callback_add_preview_arg(&$item, $key){ if ( filter_var($item, FILTER_VALIDATE_URL) !== FALSE ) { $item = add_query_arg( 'trp-edit-translation', 'preview', $item ); } } /** * Filters the output buffer of ajax calls that return json and adds the preview arg to urls * @param $output * @return string * @since 1.1.2 */ public function force_form_language_on_url_in_ajax( $output ){ if ( TRP_Gettext_Manager::is_ajax_on_frontend() && isset( $_REQUEST[ 'trp-form-language' ] ) && !empty( $_REQUEST[ 'trp-form-language' ] ) ) { $result = json_decode($output, TRUE); if ( json_last_error() === JSON_ERROR_NONE) { array_walk_recursive($result, array($this, 'callback_add_language_to_url')); $output = trp_safe_json_encode($result); } //endif } //endif return $output; } /** * Adds preview query arg to links that are url's. callback specifically for the array_walk_recursive function * @param $item * @param $key * @return string * @internal param $output * @since 1.1.2 */ function callback_add_language_to_url(&$item, $key){ if ( filter_var($item, FILTER_VALIDATE_URL) !== FALSE ) { $form_language_slug = isset( $_REQUEST[ 'trp-form-language' ] ) ? sanitize_text_field($_REQUEST[ 'trp-form-language' ]) : ''; $form_language = array_search($form_language_slug, $this->settings['url-slugs']); if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $item = $this->url_converter->get_url_for_language( $form_language, $item ); $item = str_replace('#TRPLINKPROCESSED', '', $item); } } /** * Function that reverses CDATA string replacement from the content because it breaks the renderer * @param $output * @return mixed */ public function handle_cdata( $output ){ $output = str_replace( ']]>', ']]>', $output ); return $output; } /** * Function always renders the default language wptexturize characters instead of the translated ones for secondary languages. * @param string * @param string * @param string * @param string * @return string */ function fix_wptexturize_characters( $translated, $text, $context, $domain ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $default_language= $settings["default-language"]; // it's reversed because the same string ’ is replaced differently based on context and we can't have the same key twice on an array $list_of_context_text = array( 'opening curly double quote' => '“', 'closing curly double quote' => '”', 'apostrophe' => '’', 'prime' => '′', 'double prime' => '″', 'opening curly single quote' => '‘', 'closing curly single quote' => '’', 'en dash' => '–', 'em dash' => '—', 'Comma-separated list of words to texturize in your language' => "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em", 'Comma-separated list of replacement words in your language' => '’tain’t,’twere,’twas,’tis,’twill,’til,’bout,’nuff,’round,’cause,’em' ); if( $default_language != $TRP_LANGUAGE && array_key_exists($context, $list_of_context_text) && in_array($text, $list_of_context_text) ){ return trp_x( $text, $context, '', $default_language ); } return $translated; } /** * Function that wraps the post title and the post content in a custom trp wrap trp-post-container so we can know in * the function translate_page() if a string is part of the content of a post so we can store a meta that gives the string context * @param $content * @param null $id * @return string */ function wrap_with_post_id( $content, $id = null ){ global $post, $TRP_LANGUAGE, $wp_query; if( empty($post->ID) ) return $content; //we try to wrap only the actual content of the post and not when the filters are executed in SEO plugins for example if( ( !$wp_query->in_the_loop || !is_main_query() ) && apply_filters('trp_wrap_with_post_id_overrule', true ) ) return $content; //for the_tile filter we have an $id and we can compare it with the post we are on ..to avoid wrapping titles in menus for example if( !is_null( $id ) && $id !== $post->ID ){ return $content; } if ( $TRP_LANGUAGE !== $this->settings['default-language'] ) { if ( is_singular() && !empty($post->ID)) { $content = "<trp-post-container data-trp-post-id='" . $post->ID . "'>" . $content . "</trp-post-container>";//changed " to ' to not break cases when the filter is applied inside an html attribute (title for example) } } return $content; } /** * Function that wraps around the PHP's is_numeric function and adds an additional check, * namely the option to translate numerals/numbers to be on. * @param $str * @return bool */ function trp_is_numeric($str){ if (is_numeric($str)){ if (isset($this->settings["trp_advanced_settings"]["enable_numerals_translation"]) && $this->settings["trp_advanced_settings"]["enable_numerals_translation"] === 'yes') { return false; } else { return true; } } else { return false; } } /** * Whether a text contains html tags. * Match an opening or closing tag among given html tags. * @param $string * @return bool */ public function is_html( $string ){ $pattern = '/<\/?(' . $this->common_html_tags . ')(\s[^>]*)?(\s?\/)?\>/'; return preg_match($pattern, $string, $matches); } /** * Matches the conditions set by $key_term_arrays * * See $skip_strings_containing_key_terms for config * * @param $string * @param $key_terms_arrays * @return bool */ public function contains_substrings($string, $key_terms_arrays){ foreach( $key_terms_arrays as $key_terms ) { if ( !empty( $key_terms['operator'] ) && !empty( $key_terms['terms'] ) && is_array( $key_terms ) ) { if ( $key_terms['operator'] == 'or' ){ foreach ( $key_terms['terms'] as $term ) { if ( stripos( $string, $term ) !== false ) { return true; } } } if ( $key_terms['operator'] == 'and' ){ foreach ( $key_terms['terms'] as $array_key => $term ) { if ( stripos( $string, $term ) !== false ) { unset($key_terms['terms'][$array_key]); if ( count ($key_terms['terms'] ) == 0 ){ return true; } } } } } } return false; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.28 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка