Файловый менеджер - Редактировать - /var/www/xthruster/html/wp-content/uploads/flags/xliff.tar
Назад
class-wpml-tm-string-xliff-reader.php 0000644 00000001637 14721654034 0013644 0 ustar 00 <?php class WPML_TM_String_Xliff_Reader extends WPML_TM_Xliff_Reader { /** * Retrieve the string translations from a XLIFF * * @param string $content The XLIFF representing a set of strings * * @return WP_Error|array The string translation representation or WP_Error * on failure */ public function get_data( $content ) { $xliff = $this->load_xliff( $content ); $data = array(); if ( $xliff && ! $xliff instanceof WP_Error ) { /** @var SimpleXMLElement $node */ foreach ( $xliff->{'file'}->{'body'}->children() as $node ) { $target = $this->get_xliff_node_target( $node ); if ( ! $target && $target !== '0' ) { return $this->invalid_xliff_error(); } $target = $this->replace_xliff_new_line_tag_with_new_line( $target ); $attr = $node->attributes(); $data[ (string) $attr['id'] ] = $target; } } return $data; } } class-wpml-tm-general-xliff-reader.php 0000644 00000001403 14721654034 0013742 0 ustar 00 <?php class WPML_TM_General_Xliff_Reader extends WPML_TM_Xliff_Reader { public function get_xliff_job_identifier( $content ) { $xliff = $this->load_xliff( $content ); if ( is_wp_error( $xliff ) ) { $identifier = false; } else { $identifier = $this->identifier_from_xliff( $xliff ); } return $identifier; } /** * Retrieve the translation from a XLIFF * * @param string $content The XLIFF representing a job * * @return WP_Error|array */ public function get_data( $content ) { $xliff = $this->load_xliff( $content ); if ( is_wp_error( $xliff ) ) { $data = $xliff; } else { $job = $this->get_job_for_xliff( $xliff ); $data = is_wp_error( $job ) ? $job : $this->generate_job_data( $xliff, $job ); } return $data; } } wpml-tm-xliff.php 0000644 00000014423 14721654034 0007772 0 ustar 00 <?php /** * @author OnTheGo Systems */ class WPML_TM_XLIFF { /** @var DOMElement */ private $body; /** @var DOMDocument */ private $dom; /** @var DOMDocumentType */ private $dtd; /** @var DOMElement */ private $file; /** @var DOMElement */ private $file_header; /** @var DOMElement */ private $file_reference; /** @var DOMElement */ private $phase_group; /** @var DOMElement */ private $root; /** @var array */ private $trans_units; /** @var string */ private $xliff_version; /** * WPML_TM_XLIFF constructor. * * @param string $xliff_version * @param string $xml_version * @param string $xml_encoding */ public function __construct( $xliff_version = '1.2', $xml_version = '1.0', $xml_encoding = 'utf-8' ) { $this->dom = new DOMDocument( $xml_version, $xml_encoding ); $this->root = $this->dom->createElement( 'xliff' ); $this->file = $this->dom->createElement( 'file' ); $this->file_header = $this->dom->createElement( 'header' ); $this->body = $this->dom->createElement( 'body' ); $this->xliff_version = $xliff_version; } /** * @param array $attributes * * @return $this */ public function setFileAttributes( $attributes ) { foreach ( $attributes as $name => $value ) { $this->file->setAttribute( $name, $value ); } return $this; } /** * @param array $args * * @return $this */ public function setPhaseGroup( array $args ) { if ( $args ) { $phase_items = array(); foreach ( $args as $name => $data ) { if ( $name && array_key_exists( 'process-name', $data ) && array_key_exists( 'note', $data ) && $data['note'] ) { $phase = $this->dom->createElement( 'phase' ); $phase->setAttribute( 'phase-name', $name ); $phase->setAttribute( 'process-name', $data['process-name'] ); $note = $this->dom->createElement( 'note' ); $note->nodeValue = $data['note']; $phase->appendChild( $note ); $phase_items[] = $phase; } } if ( $phase_items ) { $this->phase_group = $this->dom->createElement( 'phase-group' ); foreach ( $phase_items as $phase_item ) { $this->phase_group->appendChild( $phase_item ); } } } return $this; } /** * @param array $references * * @return $this */ public function setReferences( array $references ) { if ( $references ) { foreach ( $references as $name => $value ) { if ( ! $value ) { continue; } if ( ! $this->file_reference ) { $this->file_reference = $this->dom->createElement( 'reference' ); } $reference = $this->dom->createElement( $name ); $reference->setAttribute( 'href', $value ); $this->file_reference->appendChild( $reference ); } } return $this; } // phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid /** * Set translation units for xliff. * * @param array $trans_units Translation units. * * @return $this */ public function setTranslationUnits( $trans_units ) { // phpcs:enable if ( $trans_units ) { foreach ( $trans_units as $trans_unit ) { $trans_unit_element = $this->dom->createElement( 'trans-unit' ); if ( array_key_exists( 'attributes', $trans_unit ) && is_array( $trans_unit['attributes'] ) ) { foreach ( $trans_unit['attributes'] as $name => $value ) { $trans_unit_element->setAttribute( $name, $value ); } } $this->appendData( 'source', $trans_unit, $trans_unit_element ); $this->appendData( 'target', $trans_unit, $trans_unit_element ); if ( $trans_unit['note']['content'] ) { $note = $this->dom->createElement( 'note' ); // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $note->nodeValue = 'wrap_tag:' . $trans_unit['note']['content']; // phpcs:enable $trans_unit_element->appendChild( $note ); } $this->trans_units[] = $trans_unit_element; } } return $this; } /** * @param string $type * @param array $trans_unit * @param DOMElement $trans_unit_element */ private function appendData( $type, $trans_unit, $trans_unit_element ) { if ( array_key_exists( $type, $trans_unit ) ) { $source = $this->dom->createElement( $type ); $datatype = isset( $trans_unit['attributes']['datatype'] ) ? $trans_unit['attributes']['datatype'] : ''; $source_cdata = $this->dom->createCDATASection( $this->validate( $datatype, $trans_unit[ $type ]['content'] ) ); $source->appendChild( $source_cdata ); if ( array_key_exists( 'attributes', $trans_unit[ $type ] ) ) { foreach ( $trans_unit[ $type ]['attributes'] as $name => $value ) { $source->setAttribute( $name, $value ); } } $trans_unit_element->appendChild( $source ); } } /** * Validate content. * * @param string $datatype Type of content data. * @param string $content Content. * * @return string */ private function validate( $datatype, $content ) { if ( 'html' === $datatype ) { $validator = new WPML_TM_Validate_HTML(); $validator->validate( $content ); return $validator->get_html(); } return $content; } public function toString() { $this->compose(); $xml = $this->dom->saveXML(); return $xml ? trim( $xml ) : ''; } private function compose() { $this->dom->xmlStandalone = false; $this->setRoot( $this->xliff_version ); if ( $this->dtd ) { $this->dom->appendChild( $this->dtd ); } if ( $this->phase_group ) { $this->file_header->appendChild( $this->phase_group ); } if ( $this->file_reference ) { $this->file_header->appendChild( $this->file_reference ); } if ( $this->trans_units ) { foreach ( $this->trans_units as $trans_unit ) { $this->body->appendChild( $trans_unit ); } } $this->file->appendChild( $this->file_header ); $this->file->appendChild( $this->body ); $this->root->appendChild( $this->file ); $this->dom->appendChild( $this->root ); } private function setRoot( $version ) { if ( $version === '1.0' ) { $implementation = new DOMImplementation(); $this->dtd = $implementation->createDocumentType( 'xliff', '-//XLIFF//DTD XLIFF//EN', 'http://www.oasis-open.org/committees/xliff/documents/xliff.dtd' ); } $this->root->setAttribute( 'version', $version ); $this->root->setAttribute( 'xmlns', 'urn:oasis:names:tc:xliff:document:' . $version ); } } classs-wpml-tm-xliff-phase.php 0000644 00000000656 14721654034 0012361 0 ustar 00 <?php abstract class WPML_TM_XLIFF_Phase { public function get() { $data = $this->get_data(); if ( $data ) { return array( $this->get_phase_name() => array( 'process-name' => $this->get_process_name(), 'note' => $data, ), ); } return array(); } abstract protected function get_data(); abstract protected function get_phase_name(); abstract protected function get_process_name(); } class-wpml-tm-xliff-factory.php 0000644 00000001267 14721654034 0012544 0 ustar 00 <?php /** * @author OnTheGo Systems */ class WPML_TM_XLIFF_Factory { const WPML_XLIFF_DEFAULT_VERSION = WPML_XLIFF_DEFAULT_VERSION; const CREATE_FOR_WRITE = 'WPML_TM_Xliff_Writer'; const CREATE_FOR_FRONT_END = 'WPML_TM_Xliff_Frontend'; public function create_writer( $xliff_version = self::WPML_XLIFF_DEFAULT_VERSION ) { return new WPML_TM_Xliff_Writer( wpml_tm_load_job_factory(), $xliff_version, wpml_tm_xliff_shortcodes() ); } public function create_frontend() { global $sitepress; $support_info = new WPML_TM_Support_Info(); return new WPML_TM_Xliff_Frontend( wpml_tm_load_job_factory(), $sitepress, $support_info->is_simplexml_extension_loaded() ); } } class-wpml-tm-xliff-shared.php 0000644 00000015125 14721654034 0012341 0 ustar 00 <?php abstract class WPML_TM_Xliff_Shared extends WPML_TM_Job_Factory_User { /** @var ?WP_Error $error */ protected $error; /** @var WPML_TM_Validate_HTML */ private $validator = null; /** * @param $string * * @return mixed */ protected function replace_xliff_new_line_tag_with_new_line( $string ) { return WPML_TP_Xliff_Parser::restore_new_line( $string ); } /** * @param SimpleXMLElement $xliff * * @return string */ protected function identifier_from_xliff( $xliff ) { $file_attributes = $xliff->{'file'}->attributes(); return (string) $file_attributes['original']; } /** * @param SimpleXMLElement $xliff * * @return stdClass|WP_Error */ public function get_job_for_xliff( SimpleXMLElement $xliff ) { $identifier = $this->identifier_from_xliff( $xliff ); $job_identifier_parts = explode( '-', $identifier ); if ( count( $job_identifier_parts ) === 2 && is_numeric( $job_identifier_parts[0] ) ) { $job_id = $job_identifier_parts[0]; $job_id = apply_filters( 'wpml_job_id', $job_id ); $md5 = $job_identifier_parts[1]; /** @var stdClass $job */ $job = $this->job_factory->get_translation_job( (int) $job_id, false, 1, false ); if ( ! $job || $md5 !== md5( $job_id . $job->original_doc_id ) ) { $job = $this->does_not_belong_error(); } } else { $job = $this->invalid_xliff_error(); } return $job; } /** * @param $xliff_node * * @return string */ protected function get_xliff_node_target( $xliff_node ) { $target = ''; if ( isset( $xliff_node->target->mrk ) ) { $target = (string) $xliff_node->target->mrk; } elseif ( isset( $xliff_node->target ) ) { $target = (string) $xliff_node->target; } return $target; } /** * @param $validator WPML_TM_Validate_HTML */ public function set_validator( $validator ) { $this->validator = $validator; } /** * @return WPML_TM_Validate_HTML */ private function get_validator() { if ( null === $this->validator ) { $this->set_validator( new WPML_TM_Validate_HTML() ); } return $this->validator; } protected function generate_job_data( SimpleXMLElement $xliff, $job ) { $data = [ 'job_id' => $job->job_id, 'rid' => $job->rid, 'fields' => [], 'complete' => 1, ]; foreach ( $xliff->file->body->children() as $node ) { $attr = $node->attributes(); $type = (string) $attr['id']; $target = $this->get_xliff_node_target( $node ); if ( 'html' === (string) $attr['datatype'] ) { $target = $this->get_validator()->restore_html( $target ); } if ( ! $this->is_valid_target( $target ) ) { return $this->invalid_xliff_error( array( 'target' ) ); } foreach ( $job->elements as $element ) { if ( $element->field_type === $type ) { $target = $this->replace_xliff_new_line_tag_with_new_line( $target ); $field = array(); $field['data'] = $target; $field['finished'] = 1; $field['tid'] = $element->tid; $field['field_type'] = $element->field_type; $field['format'] = $element->field_format; $data['fields'][ $element->field_type ] = $field; break; } } } return $data; } /** * Validate XLIFF target on reading XLIFF. * * @param $target string * * @return bool */ private function is_valid_target( $target ) { return $target || '0' === $target; } protected function validate_file( $name, $content, $current_user ) { $xml = $this->check_xml_file( $name, $content ); if ( ! $xml ) { return null; } $this->error = null; if ( is_wp_error( $xml ) ) { $this->error = $xml; return null; } $job = $this->get_job_for_xliff( $xml ); if ( is_wp_error( $job ) ) { $this->error = $job; return null; } if ( ! $this->is_user_the_job_owner( $current_user, $job ) ) { $this->error = $this->not_the_job_owner_error( $job ); return null; } $job_data = $this->generate_job_data( $xml, $job ); if ( is_wp_error( $job_data ) ) { $this->error = $job_data; return null; } return array( $job, $job_data ); } /** * @param string $filename * @return bool */ function validate_file_name( $filename ) { $ignored_files = apply_filters( 'wpml_xliff_ignored_files', array( '__MACOSX' ) ); return ! ( '/' === substr( $filename, -1 ) || '/' === substr( $filename, 0, 1 ) || in_array( $filename, $ignored_files, false ) ); } protected function is_user_the_job_owner( $current_user, $job ) { return (int) $current_user->ID === (int) $job->translator_id; } protected function not_the_job_owner_error( $job ) { $message = sprintf( __( 'The translation job (%s) doesn\'t belong to you.', 'wpml-translation-management' ), $job->job_id ); return new WP_Error( 'not_your_job', $message ); } /** * @param string $name * @param string $content * * @return false|SimpleXMLElement|WP_Error */ protected function check_xml_file( $name, $content ) { set_error_handler( array( $this, 'error_handler' ) ); try { $xml = simplexml_load_string( $content ); } catch ( Exception $e ) { $xml = false; } restore_error_handler(); if ( ! $xml || ! isset( $xml->file ) ) { $xml = $this->not_xml_file_error( $name ); } return $xml; } /** * @param $errno * @param $errstr * @param $errfile * @param $errline * * @throws ErrorException */ protected function error_handler( $errno, $errstr, $errfile, $errline ) { throw new ErrorException( $errstr, $errno, 1, $errfile, $errline ); } /** * @param string $name * * @return WP_Error */ protected function not_xml_file_error( $name ) { $message = sprintf( __( '"%s" is not a valid XLIFF file.', 'wpml-translation-management' ), $name ); return new WP_Error( 'not_xml_file', $message ); } /** * @param array $missing_data * * @return WP_Error */ protected function invalid_xliff_error( array $missing_data = array() ) { $message = __( 'The uploaded xliff file does not seem to be properly formed.', 'wpml-translation-management' ); if ( $missing_data ) { $message .= '<br>' . __( 'Missing or wrong data:', 'wpml-translation-management' ); if ( count( $missing_data ) > 1 ) { $message .= '<ol>'; $message .= '<li><strong>' . implode( '</strong></li><li><strong>', $missing_data ) . '</strong></li>'; $message .= '</ol>'; } else { $message .= ' <strong>' . $missing_data[0] . '</strong>'; } } return new WP_Error( 'xliff_invalid', $message ); } /** * @return WP_Error */ protected function does_not_belong_error() { return new WP_Error( 'xliff_does_not_match', __( "The uploaded xliff file doesn't belong to this system.", 'wpml-translation-management' ) ); } } class-wpml-tm-general-xliff-import.php 0000644 00000002572 14721654034 0014022 0 ustar 00 <?php class WPML_TM_General_Xliff_Import extends WPML_TM_Job_Factory_User { /** * @var WPML_TM_Xliff_Reader_Factory $xliff_reader_factory */ private $xliff_reader_factory; /** * WPML_TM_General_Xliff_Import constructor. * * @param WPML_Translation_Job_Factory $job_factory * @param WPML_TM_Xliff_Reader_Factory $xliff_reader_factory */ public function __construct( &$job_factory, &$xliff_reader_factory ) { parent::__construct( $job_factory ); $this->xliff_reader_factory = &$xliff_reader_factory; } /** * Imports the data in the xliff string into an array representation * that fits to the given target translation id. * * @param string $xliff_string * @param int $target_translation_id * * @return WP_Error|array */ public function import( $xliff_string, $target_translation_id ) { $xliff_reader = $this->xliff_reader_factory->general_xliff_reader(); $job_data = $xliff_reader->get_data( $xliff_string ); if ( is_wp_error( $job_data ) ) { $job = $this->job_factory->job_by_translation_id( $target_translation_id ); if ( $job && ( $id_string = $xliff_reader->get_xliff_job_identifier( $xliff_string ) ) !== false ) { $job_data = $xliff_reader->get_data( str_replace( $id_string, $job->get_id() . '-' . md5( $job->get_id() . $job->get_original_element_id() ), $xliff_string ) ); } } return $job_data; } } class-wpml-tm-validate-html.php 0000644 00000027571 14721654034 0012530 0 ustar 00 <?php /** * Class WPML_TM_Validate_HTML */ class WPML_TM_Validate_HTML { /** @var string Validated html */ private $html = ''; /** @var array Tags currently open */ private $tags = array(); /** @var int Number of errors */ private $error_count = 0; /** * Get validated html. * * @return string */ public function get_html() { return $this->html; } /** * Validate html. * * @param string $html HTML to process. * * @return int Number of errors. */ public function validate( $html ) { $html = $this->hide_wp_bugs( $html ); $html = $this->hide_cdata( $html ); $html = $this->hide_comments( $html ); $html = $this->hide_self_closing_tags( $html ); $html = $this->hide_scripts( $html ); $html = $this->hide_styles( $html ); $processed_html = ''; $html_arr = array( 'processed' => $processed_html, 'next' => $html, ); while ( '' !== $html_arr['next'] ) { $html_arr = $this->validate_next( $html_arr['next'] ); if ( $html_arr ) { $processed_html .= $html_arr['processed']; } } $html = $processed_html; $html = $this->restore_styles( $html ); $html = $this->restore_scripts( $html ); $html = $this->restore_self_closing_tags( $html ); $html = $this->restore_comments( $html ); $html = $this->restore_wp_bugs( $html ); $this->html = $html; return $this->error_count; } /** * Validate first tag in html flow and return processed html and rest. * In processed part broken html is replaced by wpml comment. * * @param string $html HTML to process. * * @return array|null */ private function validate_next( $html ) { $regs = array(); // Get first opening or closing tag. $pattern = '<\s*?([a-z]+|/[a-z]+)((?:.|\s)*?)>'; mb_eregi( $pattern, $html, $regs ); if ( $regs ) { $full_tag = $regs[0]; $pos = mb_strpos( $html, $full_tag ); $next_html = mb_substr( $html, $pos + mb_strlen( $full_tag ) ); $tag = $regs[1]; $result = true; if ( '/' === mb_substr( $tag, 0, 1 ) ) { $result = $this->close_tag( mb_substr( $tag, 1 ) ); } else { $this->open_tag( $tag ); } if ( $result ) { $processed_html = mb_substr( $html, 0, $pos + mb_strlen( $full_tag ) ); } else { $processed_html = mb_substr( $html, 0, (int) $pos ) . '<!-- wpml:html_fragment ' . $full_tag . ' -->'; } return array( 'processed' => $processed_html, 'next' => $next_html, ); } return array( 'processed' => $html, 'next' => '', ); } /** * Convert WP bugs into wpml commented bugs. * * @param string $html HTML to process. * * @return string */ private function hide_wp_bugs( $html ) { // WP bug fix for comments - in case you REALLY meant to type '< !--' $html = str_replace( '< !--', '< !--', $html ); // WP bug fix for LOVE <3 (and other situations with '<' before a number) $pattern = '<([0-9]{1})'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_wp_bug_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert WP bugs into wpml commented bugs. * * @param array $matches * * @return string */ public function hide_wp_bug_callback( $matches ) { return '<!-- wpml:wp_bug ' . $matches[0] . ' -->'; } /** * Convert wpml commented bugs to WP bugs. * * @param $html * * @return string */ private function restore_wp_bugs( $html ) { $pattern = '<!-- wpml:wp_bug (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_bug_callback' ), $html, 'msri' ); $html = $filtered === false ? $html : $filtered; $html = str_replace( '< !--', '< !--', $html ); return $html; } /** * Callback to convert wpml commented bugs to WP bugs. * * @param array $matches * * @return mixed */ public function restore_bug_callback( $matches ) { return $matches[1]; } /** * Convert HTML comments into wpml comments. * * @param string $html HTML to process. * * @return string */ private function hide_comments( $html ) { $pattern = '<!--(.*?)-->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_comment_callback' ), $html, 'msri' ); $html = $filtered === false ? $html : $filtered; $pattern = '<!((?!-- wpml:).*?)>'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_declaration_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert HTML comment to wpml comment. * * @param array $matches * * @return string */ public function hide_comment_callback( $matches ) { return '<!-- wpml:html_comment ' . base64_encode( $matches[0] ) . ' -->'; } /** * Callback to convert HTML declaration to wpml declaration. * * @param array $matches * * @return string */ public function hide_declaration_callback( $matches ) { return '<!-- wpml:html_declaration ' . base64_encode( $matches[0] ) . ' -->'; } /** * Convert wpml comments to HTML comments. * * @param string $html * * @return string */ private function restore_comments( $html ) { $pattern = '<!-- wpml:html_comment (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_encoded_content_callback' ), $html, 'msri' ); $html = $filtered === false ? $html : $filtered; $pattern = '<!-- wpml:html_declaration (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_encoded_content_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert wpml base64 encoded content. * * @param array $matches * * @return string */ public function restore_encoded_content_callback( $matches ) { return base64_decode( $matches[1] ); } /** * Convert self-closing tags to wpml self-closing tags. * * @param string $html HTML to process. * * @return string */ private function hide_self_closing_tags( $html ) { $self_closing_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source', 'track', 'wbr', 'command', 'keygen', 'menuitem', 'path', 'polyline', ); foreach ( $self_closing_tags as $self_closing_tag ) { $pattern = '<\s*?' . $self_closing_tag . '((?:.|\s)*?)(>|/>)'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_sct_callback' ), $html, 'msri' ); $html = $filtered === false ? $html : $filtered; } $pattern = '<\s*?[^>]*/>'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_sct_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert self-closing tags to wpml self-closing tags. * * @param $matches * * @return string */ public function hide_sct_callback( $matches ) { return '<!-- wpml:html_self_closing_tag ' . str_replace( array( '<', '>' ), '', $matches[0] ) . ' -->'; } /** * Convert wpml self-closing tags to HTML self-closing tags. * * @param $html * * @return string */ private function restore_self_closing_tags( $html ) { $pattern = '<!-- wpml:html_self_closing_tag (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_sct_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert wpml self-closing tags to self-closing tags. * * @param $matches * * @return string */ public function restore_sct_callback( $matches ) { return '<' . $matches[1] . '>'; } /** * Convert wpml comments to initial HTML. * * @param $html * * @return false|string */ public function restore_html( $html ) { $html = $this->restore_cdata( $html ); $html = $this->restore_html_fragments( $html ); return $html; } /** * Convert wpml fragments to HTML fragments. * * @param $html * * @return false|string */ private function restore_html_fragments( $html ) { $pattern = '<!-- wpml:html_fragment (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_html_fragment_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert wpml fragment to HTML fragment. * * @param $matches * * @return string */ public function restore_html_fragment_callback( $matches ) { return $matches[1]; } /** * Convert scripts to wpml scripts. * * @param string $html HTML to process. * * @return string */ private function hide_scripts( $html ) { $pattern = '<\s*?script\s*?>((?:.|\s)*?)</script\s*?>'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_script_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert script to wpml script. * * @param array $matches * * @return string */ public function hide_script_callback( $matches ) { return '<!-- wpml:script ' . base64_encode( $matches[0] ) . ' -->'; } /** * Convert wpml scripts to scripts. * * @param $html * * @return string */ private function restore_scripts( $html ) { $pattern = '<!-- wpml:script (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_encoded_content_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Convert CDATA to wpml cdata. * * @param string $html HTML to process. * * @return string */ private function hide_cdata( $html ) { $pattern = '<!\[CDATA\[((?:.|\s)*?)\]\]>'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_cdata_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert CDATA to wpml cdata. * * @param array $matches * * @return string */ public function hide_cdata_callback( $matches ) { return '<!-- wpml:cdata ' . base64_encode( $matches[0] ) . ' -->'; } /** * Convert wpml cdata to CDATA. * * @param $html * * @return string */ private function restore_cdata( $html ) { $pattern = '<!-- wpml:cdata (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_encoded_content_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Convert styles to wpml scripts. * * @param string $html HTML to process. * * @return string */ private function hide_styles( $html ) { $pattern = '<\s*?style\s*?>((?:.|\s)*?)</style\s*?>'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'hide_style_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Callback to convert style to wpml style. * * @param array $matches * * @return string */ public function hide_style_callback( $matches ) { return '<!-- wpml:style ' . base64_encode( $matches[0] ) . ' -->'; } /** * Convert wpml styles to styles. * * @param $html * * @return string */ private function restore_styles( $html ) { $pattern = '<!-- wpml:style (.*?) -->'; $filtered = mb_ereg_replace_callback( $pattern, array( $this, 'restore_encoded_content_callback' ), $html, 'msri' ); return $filtered === false ? $html : $filtered; } /** * Open tag encountered in html. * * @param string $tag Tag name. */ private function open_tag( $tag ) { $tag = mb_strtolower( $tag ); array_push( $this->tags, $tag ); } /** * Close tag encountered in html. * * @param string $tag Tag name. * * @return bool Closed successfully. */ private function close_tag( $tag ) { $tag = mb_strtolower( $tag ); $last_tag = end( $this->tags ); if ( $last_tag === $tag ) { array_pop( $this->tags ); return true; } else { $this->error_count ++; if ( in_array( $tag, $this->tags, true ) ) { do { array_pop( $this->tags ); } while ( $this->tags && end( $this->tags ) !== $tag ); array_pop( $this->tags ); } return false; } } } class-wpml-tm-xliff-post-type.php 0000644 00000000665 14721654034 0013042 0 ustar 00 <?php /** * @author OnTheGo Systems */ class WPML_TM_XLIFF_Post_Type extends WPML_TM_XLIFF_Phase { private $post_type; public function __construct( $post_type = '' ) { $this->post_type = $post_type; } /** * @return string */ protected function get_data() { return $this->post_type; } protected function get_phase_name() { return 'post_type'; } protected function get_process_name() { return 'Post type'; } } class-wpml-tm-xliff-reader.php 0000644 00000001301 14721654034 0012324 0 ustar 00 <?php abstract class WPML_TM_Xliff_Reader extends WPML_TM_Xliff_Shared { /** * @param string $content Xliff file string content * * @return array */ abstract public function get_data( $content ); /** * Parse a XML containing the XLIFF * * @param string $content * * @return SimpleXMLElement|WP_Error The parsed XLIFF or a WP error in case it could not be parsed */ public function load_xliff( $content ) { try { $xml = simplexml_load_string( $content ); } catch ( Exception $e ) { $xml = false; } return $xml ? $xml : new WP_Error( 'not_xml_file', sprintf( __( 'The xliff file could not be read.', 'wpml-translation-management' ) ) ); } } class-wpml-tm-xliff-reader-factory.php 0000644 00000001011 14721654034 0013767 0 ustar 00 <?php class WPML_TM_Xliff_Reader_Factory extends WPML_TM_Job_Factory_User { /** * @return WPML_TM_General_Xliff_Reader */ public function general_xliff_reader() { return new WPML_TM_General_Xliff_Reader( $this->job_factory ); } public function general_xliff_import() { return new WPML_TM_General_Xliff_Import( $this->job_factory, $this ); } /** * @return WPML_TM_String_Xliff_Reader */ public function string_xliff_reader() { return new WPML_TM_String_Xliff_Reader( $this->job_factory ); } } class-wpml-tm-xliff-frontend.php 0000644 00000054500 14721654034 0012712 0 ustar 00 <?php /** * WPML_TM_Xliff_Frontend class file * * @package wpml-translation-management */ use WPML\FP\Obj; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once WPML_TM_PATH . '/inc/wpml_zip.php'; /** * Class WPML_TM_Xliff_Frontend */ class WPML_TM_Xliff_Frontend extends WPML_TM_Xliff_Shared { /** * Success admin notices * * @var array */ private $success; /** * Attachments * * @var array */ private $attachments = array(); /** * SitePress instance * * @var SitePress */ private $sitepress; /** * Name of archive * * @var string */ private $export_archive_name; /** * Priority of late initialisation * * @var int */ private $late_init_priority = 9999; /** * Is simple xml turned on * * @var bool */ private $simplexml_on; /** * WPML_TM_Xliff_Frontend constructor * * @param WPML_Translation_Job_Factory $job_factory Job factory. * @param SitePress $sitepress SitePress instance. * @param boolean $simplexml_on Is simple xml turned on. */ public function __construct( WPML_Translation_Job_Factory $job_factory, SitePress $sitepress, $simplexml_on ) { parent::__construct( $job_factory ); $this->sitepress = $sitepress; $this->simplexml_on = $simplexml_on; } /** * Get available xliff versions * * @return array */ public function get_available_xliff_versions() { return array( '10' => '1.0', '11' => '1.1', '12' => '1.2', ); } /** * Get init priority * * @return int */ public function get_init_priority() { return isset( $_POST['xliff_upload'] ) || ( isset( $_GET['wpml_xliff_action'] ) && $_GET['wpml_xliff_action'] === 'download' ) || isset( $_POST['wpml_xliff_export_all_filtered'] ) ? $this->get_late_init_priority() : 10; } /** * Get late init priority * * @return int */ public function get_late_init_priority() { return $this->late_init_priority; } /** * Init class * * @return bool * @throws Exception Throws an exception in case of errors. */ public function init() { $this->attachments = array(); $this->error = null; if ( $this->sitepress->get_wp_api()->is_admin() ) { add_action( 'admin_head', array( $this, 'js_scripts' ) ); add_action( 'wp_ajax_set_xliff_options', array( $this, 'ajax_set_xliff_options', ), 10, 0 ); if ( ! $this->sitepress->get_setting( 'xliff_newlines' ) ) { $this->sitepress->set_setting( 'xliff_newlines', WPML_XLIFF_TM_NEWLINES_ORIGINAL, true ); } if ( ! $this->sitepress->get_setting( 'tm_xliff_version' ) ) { $this->sitepress->set_setting( 'tm_xliff_version', '12', true ); } if ( 1 < count( $this->sitepress->get_languages( false, true ) ) ) { add_filter( 'wpml_translation_queue_actions', array( $this, 'translation_queue_add_actions', ) ); add_action( 'wpml_xliff_select_actions', array( $this, 'translation_queue_xliff_select_actions', ), 10, 4 ); add_action( 'wpml_translation_queue_after_display', array( $this, 'translation_queue_after_display', ), 10, 1 ); add_action( 'wpml_translator_notification', array( $this, 'translator_notification', ), 10, 0 ); add_filter( 'wpml_new_job_notification', array( $this, 'new_job_notification', ), 10, 2 ); add_filter( 'wpml_new_job_notification_attachments', array( $this, 'new_job_notification_attachments', ) ); } if ( isset( $_GET['wpml_xliff_action'] ) && $_GET['wpml_xliff_action'] === 'download' && wp_verify_nonce( $_GET['nonce'], 'xliff-export' ) ) { $archive = $this->get_xliff_archive( Obj::propOr( '', 'xliff_version', $_GET ), explode( ',', Obj::propOr( '', 'jobs', $_GET ) ) ); $this->stream_xliff_archive( $archive ); } add_action( 'wp_ajax_wpml_xliff_upload', function () { if ( $this->import_xliff( Obj::propOr( [], 'import', $_FILES ) ) ) { wp_send_json_success( $this->success ); } else { wp_send_json_error( $this->error ); } } ); } return true; } /** * Set xliff options */ public function ajax_set_xliff_options() { check_ajax_referer( 'icl_xliff_options_form_nonce', 'security' ); $newlines = isset( $_POST['icl_xliff_newlines'] ) ? (int) $_POST['icl_xliff_newlines'] : 0; $this->sitepress->set_setting( 'xliff_newlines', $newlines, true ); $version = isset( $_POST['icl_xliff_version'] ) ? (int) $_POST['icl_xliff_version'] : 0; $this->sitepress->set_setting( 'tm_xliff_version', $version, true ); wp_send_json_success( array( 'message' => 'OK', 'newlines_saved' => $newlines, 'version_saved' => $version, ) ); } /** * New job notification * * @param array $mail Email content. * @param int $job_id Job id. * * @return array */ public function new_job_notification( $mail, $job_id ) { $tm_settings = $this->sitepress->get_setting( 'translation-management', array() ); if ( isset( $tm_settings['notification']['include_xliff'] ) && $tm_settings['notification']['include_xliff'] ) { $xliff_version = $this->get_user_xliff_version(); $xliff_version = $xliff_version ?: WPML_XLIFF_DEFAULT_VERSION; $xliff_file = $this->get_xliff_file( $job_id, $xliff_version ); $temp_dir = get_temp_dir(); $file_name = $temp_dir . get_bloginfo( 'name' ) . '-translation-job-' . $job_id . '.xliff'; $fh = fopen( $file_name, 'w' ); if ( $fh ) { fwrite( $fh, $xliff_file ); fclose( $fh ); $mail['attachment'] = $file_name; $this->attachments[ $job_id ] = $file_name; $mail['body'] .= __( ' - A xliff file is attached.', 'wpml-translation-management' ); } } return $mail; } /** * Get zip name from jobs * * @param array $job_ids Job ids. * * @return string */ private function get_zip_name_from_jobs( $job_ids ) { $min_job = min( $job_ids ); $max_job = max( $job_ids ); if ( $max_job === $min_job ) { return get_bloginfo( 'name' ) . '-translation-job-' . $max_job . '.zip'; } else { return get_bloginfo( 'name' ) . '-translation-job-' . $min_job . '-' . $max_job . '.zip'; } } /** * New job notification attachments * * @param array $attachments Job notification attachments. * * @return array */ public function new_job_notification_attachments( $attachments ) { $found = false; $archive = new wpml_zip(); foreach ( $attachments as $index => $attachment ) { if ( in_array( $attachment, $this->attachments, true ) ) { $fh = fopen( $attachment, 'r' ); if ( $fh ) { $xliff_file = fread( $fh, (int) filesize( $attachment ) ); fclose( $fh ); $archive->addFile( $xliff_file, basename( $attachment ) ); unset( $attachments[ $index ] ); $found = true; } } } if ( $found ) { // Add the zip file to the attachments. $archive_data = $archive->getZipData(); $temp_dir = get_temp_dir(); $file_name = $temp_dir . $this->get_zip_name_from_jobs( array_keys( $this->attachments ) ); $fh = fopen( $file_name, 'w' ); if ( $fh ) { fwrite( $fh, $archive_data ); fclose( $fh ); $attachments[] = $file_name; } } return $attachments; } /** * Get xliff file * * @param int $job_id Job id. * @param string $xliff_version Xliff version. * * @return string */ private function get_xliff_file( $job_id, $xliff_version = WPML_XLIFF_DEFAULT_VERSION ) { return wpml_tm_xliff_factory() ->create_writer( $xliff_version ) ->generate_job_xliff( $job_id ); } /** * Get xliff archive * * @param string $xliff_version Xliff version. * @param array|null $job_ids Job ids. * * @return wpml_zip * * @throws Exception Throws an exception in case of errors. */ public function get_xliff_archive( $xliff_version, $job_ids = array() ) { global $wpdb, $current_user; if ( empty( $job_ids ) && isset( $_GET['xliff_export_data'] ) ) { // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode $data = json_decode( base64_decode( $_GET['xliff_export_data'] ) ); // phpcs:enable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode $job_ids = isset( $data->job ) ? array_keys( (array) $data->job ) : array(); } $archive = new wpml_zip(); foreach ( $job_ids as $job_id ) { $xliff_file = $this->get_xliff_file( $job_id, $xliff_version ); // Assign the job to this translator. $rid = $wpdb->get_var( $wpdb->prepare( "SELECT rid FROM {$wpdb->prefix}icl_translate_job WHERE job_id=%d ", $job_id ) ); $data_value = array( 'translator_id' => $current_user->ID ); $data_where = array( 'job_id' => $job_id ); $wpdb->update( $wpdb->prefix . 'icl_translate_job', $data_value, $data_where ); $data_where = array( 'rid' => $rid ); $wpdb->update( $wpdb->prefix . 'icl_translation_status', $data_value, $data_where ); $archive->addFile( $xliff_file, get_bloginfo( 'name' ) . '-translation-job-' . $job_id . '.xliff' ); } $this->export_archive_name = $this->get_zip_name_from_jobs( $job_ids ); $archive->finalize(); return $archive; } /** * Stream xliff archive * * @param wpml_zip $archive Zip archive. * * @throws Exception Throws an exception in case of errors. */ private function stream_xliff_archive( $archive ) { if ( is_a( $archive, 'wpml_zip' ) ) { if ( defined( 'WPML_SAVE_XLIFF_PATH' ) && trim( WPML_SAVE_XLIFF_PATH ) ) { $this->save_zip_file( WPML_SAVE_XLIFF_PATH, $archive ); } $archive->sendZip( $this->export_archive_name ); } exit; } /** * Save zip file * * @param string $path Where to save the archive. * @param wpml_zip $archive Zip archive. */ private function save_zip_file( $path, $archive ) { $path = trailingslashit( $path ); if ( ! is_dir( $path ) ) { $result = mkdir( $path ); if ( ! $result ) { return; } } $archive->setZipFile( $path . $this->export_archive_name ); } /** * Stops any redirects from happening when we call the * translation manager to save the translations. * * @return null */ public function stop_redirect() { return null; } /** * Import xliff file * * @param array $file Xliff file data. * * @return bool|WP_Error */ private function import_xliff( $file ) { global $current_user; // We don't want any redirects happening when we save the translation. add_filter( 'wp_redirect', array( $this, 'stop_redirect' ) ); $this->success = array(); $contents = array(); if ( 0 === (int) $file['size'] ) { $this->error = new WP_Error( 'empty_file', __( 'You are trying to import an empty file.', 'wpml-translation-management' ) ); return false; } elseif ( isset( $file['tmp_name'] ) && $file['tmp_name'] ) { $fh = fopen( $file['tmp_name'], 'r' ); $data = false; if ( $fh ) { $data = fread( $fh, 4 ); fclose( $fh ); } if ( $data && $data[0] == 'P' && $data[1] == 'K' && $data[2] == chr( 03 ) && $data[3] == chr( 04 ) ) { if ( class_exists( 'ZipArchive' ) ) { $z = new ZipArchive(); $zopen = $z->open( $file['tmp_name'], 4 ); if ( true !== $zopen ) { $this->error = new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.', 'wpml-translation-management' ) ); return false; } $empty_files = array(); for ( $i = 0; $i < $z->numFiles; $i ++ ) { if ( ! $info = $z->statIndex( $i ) ) { $this->error = new WP_Error( 'stat_failed', __( 'Could not retrieve file from archive.', 'wpml-translation-management' ) ); return false; } if ( $this->is_directory( $info['name'] ) ) { continue; } $content = $z->getFromIndex( $i ); if ( false === (bool) $content ) { $empty_files[] = $info['name']; } $contents[ $info['name'] ] = $content; } if ( $empty_files ) { $this->error = new WP_Error( 'extract_failed', __( 'The archive contains one or more empty files.', 'wpml-translation-management' ), $empty_files ); return false; } } else { require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; $archive = new PclZip( $file['tmp_name'] ); // Is the archive valid? $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); if ( false == $archive_files ) { $this->error = new WP_Error( 'incompatible_archive', __( 'You are trying to import an incompatible Archive.', 'wpml-translation-management' ), $archive->errorInfo( true ) ); return false; } if ( 0 === count( $archive_files ) ) { $this->error = new WP_Error( 'empty_archive', __( 'You are trying to import an empty archive.', 'wpml-translation-management' ) ); return false; } $empty_files = array(); foreach ( $archive_files as $content ) { if ( $this->is_directory( $content['filename'] ) ) { continue; } if ( false === (bool) $content['content'] ) { $empty_files[] = $content['filename']; } $contents[ $content['filename'] ] = $content['content']; } if ( $empty_files ) { $this->error = new WP_Error( 'extract_failed', __( 'The archive contains one or more empty files.', 'wpml-translation-management' ), $empty_files ); return false; } } } else { $fh = fopen( $file['tmp_name'], 'r' ); if ( $fh ) { $data = fread( $fh, $file['size'] ); fclose( $fh ); $contents[ $file['name'] ] = $data; } } foreach ( $contents as $name => $content ) { if ( $this->validate_file_name( (string) $name ) ) { list( $job, $job_data ) = $this->validate_file( $name, $content, $current_user ); if ( null !== $this->error ) { return $job_data; } kses_remove_filters(); wpml_tm_save_data( $job_data ); kses_init(); // translators: %s: job id. $this->success[] = $job->job_id; } } return true; } return false; } /** * Translation queue actions * * @param array $actions Actions. * @param string $action_name Action name. * @param array $translation_jobs Translation jobs. * @param string $action */ public function translation_queue_xliff_select_actions( $actions, $action_name, $translation_jobs, $action ) { if ( $this->has_translation_jobs( $translation_jobs ) && sizeof( $actions ) > 0 ) : $currentAction = $action ?: false; ?> <div class="alignleft actions"> <select name="<?php echo esc_html( $action_name ); ?>"> <option value="-1" <?php echo $currentAction == false ? "selected='selected'" : ''; ?>><?php _e( 'Bulk Actions' ); ?></option> <?php foreach ( $actions as $key => $action ) : ?> <option <?php disabled( ! $this->simplexml_on ); ?> value="<?php echo $key; ?>" <?php echo $currentAction == $key && $this->simplexml_on ? "selected='selected'" : ''; ?>><?php echo $action; ?></option> <?php endforeach; ?> </select> <input id="js-wpml-do-action" type="submit" value="<?php esc_attr_e( 'Apply' ); ?>" name="do<?php echo esc_html( $action_name ); ?>" class="button-secondary action"/> </div> <?php endif; } /** * Has translation jobs * * @param array $translation_jobs Translation jobs. * * @return bool */ private function has_translation_jobs( $translation_jobs ) { return $translation_jobs && array_key_exists( 'jobs', $translation_jobs ) && $translation_jobs['jobs']; } /** * Get xliff version select options * * @return string */ private function get_xliff_version_select_options() { $output = ''; $user_version = (int) $this->get_user_xliff_version(); foreach ( $this->get_available_xliff_versions() as $value => $label ) { $user_version = false === $user_version ? $value : $user_version; $output .= '<option value="' . $value . '"'; $output .= $user_version === $value ? 'selected="selected"' : ''; $output .= '>XLIFF ' . $label . '</option>'; } return $output; } /** * Adds the various possible XLIFF versions to translations queue page's export actions on display * * @param array $actions Actions. * * @return array */ public function translation_queue_add_actions( $actions ) { foreach ( $this->get_available_xliff_versions() as $key => $value ) { // translators: %s: XLIFF version. $actions[ $key ] = sprintf( __( 'Export XLIFF %s', 'wpml-translation-management' ), $value ); } return $actions; } /** * Show error messages in admin notices */ public function admin_notices_error() { if ( is_wp_error( $this->error ) ) { ?> <div class="message error"> <p><?php echo $this->error->get_error_message(); ?></p> <?php if ( $this->error->get_error_data() ) { ?> <ol> <li><?php echo implode( '</li><li>', $this->error->get_error_data() ); ?></li> </ol> <?php } ?> </div> <?php } } /** * Show success messages in admin notices */ public function admin_notices_success() { ?> <div class="message updated"><p> <ul> <?php foreach ( $this->success as $message ) { echo '<li>' . $message . '</li>'; } ?> </ul> </p></div> <?php } /** * Check translation queue after display * * @param array $translation_jobs Translation jobs. */ public function translation_queue_after_display( $translation_jobs = array() ) { if ( ! $this->has_translation_jobs( $translation_jobs ) ) { return; } $export_label = esc_html__( 'Export all jobs:', 'wpml-translation-management' ); $cookie_filters = WPML_Translations_Queue::get_cookie_filters(); if ( $cookie_filters ) { $type = __( 'All types', 'wpml-translation-management' ); if ( ! empty( $cookie_filters['type'] ) ) { $post_slug = preg_replace( '/^post_|^package_/', '', $cookie_filters['type'], 1 ); $post_types = $this->sitepress->get_translatable_documents( true ); $post_types = apply_filters( 'wpml_get_translatable_types', $post_types ); if ( array_key_exists( $post_slug, $post_types ) ) { $type = $post_types[ $post_slug ]->label; } } $from = ! empty( $cookie_filters['from'] ) ? $this->sitepress->get_display_language_name( $cookie_filters['from'] ) : __( 'Any language', 'wpml-translation-management' ); $to = ! empty( $cookie_filters['to'] ) ? $this->sitepress->get_display_language_name( $cookie_filters['to'] ) : __( 'Any language', 'wpml-translation-management' ); $status = ! empty( $cookie_filters['status'] ) && (int) $cookie_filters['status'] !== ICL_TM_WAITING_FOR_TRANSLATOR ? TranslationManagement::status2text( $cookie_filters['status'] ) : ( ! empty( $cookie_filters['status'] ) ? __( 'Available to translate', 'wpml-translation-management' ) : 'All statuses' ); $export_label = sprintf( // translators: %1: post type, %2: from language, %3: to language, %4: status. esc_html__( 'Export all filtered jobs of %1$s from %2$s to %3$s in %4$s:', 'wpml-translation-management' ), '<b>' . $type . '</b>', '<b>' . $from . '</b>', '<b>' . $to . '</b>', '<b>' . $status . '</b>' ); } ?> <br/> <table class="widefat"> <thead> <tr> <th><?php esc_html_e( 'Import / Export XLIFF', 'wpml-translation-management' ); ?></th> </tr> </thead> <tbody> <tr> <td> <?php if ( ! $this->simplexml_on ) : ?> <div class="otgs-notice error"> <p> <strong><?php esc_html_e( 'SimpleXML missing!', 'wpml-translation-management' ); ?></strong> </p> <p> <?php esc_html_e( 'SimpleXML extension is required for using XLIFF files in WPML Translation Management.', 'wpml-translation-management' ); ?> <a href="https://wpml.org/?page_id=716"><?php esc_html_e( 'WPML Minimum Requirements', 'wpml-translation-management' ); ?></a> </p> </div> <?php endif; ?> <form method="post" id="translation-xliff-export-all-filtered" action=""> <label for="wpml_xliff_export_all_filtered"><?php echo $export_label; ?></label> <select name="xliff_version" class="select" <?php disabled( ! $this->simplexml_on ); ?>> <?php echo $this->get_xliff_version_select_options(); ?> </select> <input type="submit" value="<?php esc_attr_e( 'Export', 'wpml-translation-management' ); ?>" <?php disabled( ! $this->simplexml_on ); ?> name="wpml_xliff_export_all_filtered" id="xliff_download" class="button-secondary action"/> <input type="hidden" value="<?php echo wp_create_nonce( 'xliff-export-all-filtered' ); ?>" name="nonce"> </form> <hr> <form enctype="multipart/form-data" method="post" id="translation-xliff-upload" action=""> <label for="upload-xliff-file"><?php _e( 'Select the xliff file or zip file to upload from your computer: ', 'wpml-translation-management' ); ?></label> <input type="file" id="upload-xliff-file" name="import" <?php disabled( ! $this->simplexml_on ); ?> /> <input type="submit" value="<?php _e( 'Upload', 'wpml-translation-management' ); ?>" name="xliff_upload" id="xliff_upload" class="button-secondary action" <?php disabled( ! $this->simplexml_on ); ?> /> </form> </td> </tr> </tbody> </table> <?php } /** * Print online js script */ public function js_scripts() { ?> <script type="text/javascript"> var wpml_xliff_ajax_nonce = '<?php echo wp_create_nonce( 'icl_xliff_options_form_nonce' ); ?>'; </script> <?php } /** * Provide translator notification */ public function translator_notification() { $checked = $this->sitepress->get_setting( 'include_xliff_in_notification' ) ? 'checked="checked"' : ''; ?> <input type="checkbox" name="include_xliff" id="icl_include_xliff" value="1" <?php echo $checked; ?>/> <label for="icl_include_xliff"><?php _e( 'Include XLIFF files in notification emails', 'wpml-translation-management' ); ?></label> <?php } /** * Get user xliff version * * @return string|false */ private function get_user_xliff_version() { return $this->sitepress->get_setting( 'tm_xliff_version', false ); } /** * Check if argument is a directory * * @param string $path * @return bool */ private function is_directory( $path ) { return '/' === substr( $path, -1 ); } } class-wpml-tm-xliff-writer.php 0000644 00000033636 14721654034 0012416 0 ustar 00 <?php /** * @package wpml-core */ use WPML\FP\Obj; use WPML\TM\Jobs\FieldId; class WPML_TM_Xliff_Writer { const TAB = "\t"; protected $job_factory; private $xliff_version; private $xliff_shortcodes; private $translator_notes; /** * WPML_TM_xliff constructor. * * @param WPML_Translation_Job_Factory $job_factory * @param string $xliff_version * @param \WPML_TM_XLIFF_Shortcodes|null $xliff_shortcodes */ public function __construct( WPML_Translation_Job_Factory $job_factory, $xliff_version = TRANSLATION_PROXY_XLIFF_VERSION, WPML_TM_XLIFF_Shortcodes $xliff_shortcodes = null ) { $this->job_factory = $job_factory; $this->xliff_version = $xliff_version; if ( ! $xliff_shortcodes ) { $xliff_shortcodes = wpml_tm_xliff_shortcodes(); } $this->xliff_shortcodes = $xliff_shortcodes; $this->translator_notes = new WPML_TM_XLIFF_Translator_Notes(); } /** * Generate a XLIFF file for a given job. * * @param int $job_id * * @return resource XLIFF representation of the job */ public function get_job_xliff_file( $job_id ) { return $this->generate_xliff_file( $this->generate_job_xliff( $job_id ) ); } /** * Generate a XLIFF string for a given post or external type (e.g. package) job. * * @param int $job_id * @param bool $apply_memory * * @return string XLIFF representation of the job */ public function generate_job_xliff( $job_id, $apply_memory = true ) { /** @var TranslationManagement $iclTranslationManagement */ global $iclTranslationManagement; // don't include not-translatable and don't auto-assign $job = $iclTranslationManagement->get_translation_job( (int) $job_id, false, false, 1 ); $translation_units = $this->get_job_translation_units_data( $job, $apply_memory ); $original = $job_id . '-' . md5( $job_id . $job->original_doc_id ); $original_post_type = isset( $job->original_post_type ) ? $job->original_post_type : null; $external_file_url = $this->get_external_url( $job ); $this->get_translator_notes( $job ); $xliff = $this->generate_xliff( $original, $job->source_language_code, $job->language_code, $translation_units, $external_file_url, $original_post_type ); return $xliff; } /** * Generate a XLIFF file for a given set of strings. * * @param array $strings * @param string $source_language * @param string $target_language * * @return resource XLIFF file */ public function get_strings_xliff_file( $strings, $source_language, $target_language ) { $strings = $this->pre_populate_strings_with_translation_memory( $strings, $source_language, $target_language ); return $this->generate_xliff_file( $this->generate_xliff( uniqid( 'string-', true ), $source_language, $target_language, $this->generate_strings_translation_units_data( $strings ) ) ); } private function generate_xliff( $original_id, $source_language, $target_language, array $translation_units = array(), $external_file_url = null, $original_post_type = null ) { $xliff = new WPML_TM_XLIFF( $this->get_xliff_version(), '1.0', 'utf-8' ); $phase_group = array(); $phase_group = array_merge( $phase_group, $this->xliff_shortcodes->get() ); $phase_group = array_merge( $phase_group, $this->translator_notes->get() ); $post_type_phase = new WPML_TM_XLIFF_Post_Type( $original_post_type ); $phase_group = array_merge( $phase_group, $post_type_phase->get() ); $string = $xliff ->setFileAttributes( array( 'original' => $original_id, 'source-language' => $source_language, 'target-language' => $target_language, 'datatype' => 'plaintext', ) ) ->setReferences( array( 'external-file' => $external_file_url, ) ) ->setPhaseGroup( $phase_group ) ->setTranslationUnits( $translation_units ) ->toString(); return $string; } private function get_xliff_version() { switch ( $this->xliff_version ) { case '10': return '1.0'; case '11': return '1.1'; case '12': default: return '1.2'; } } /** * Generate translation units for a given set of strings. * * The units are the actual content to be translated * Represented as a source and a target * * @param array $strings * * @return array The translation units representation */ private function generate_strings_translation_units_data( $strings ) { $translation_units = array(); foreach ( $strings as $string ) { $id = 'string-' . $string->id; $translation_units[] = $this->get_translation_unit_data( $id, 'string', $string->value, $string->translation, $string->translated_from_memory ); } return $translation_units; } /** * @param stdClass[] $strings * @param string $source_lang * @param string $target_lang * * @return stdClass[] */ private function pre_populate_strings_with_translation_memory( $strings, $source_lang, $target_lang ) { $strings_to_translate = wp_list_pluck( $strings, 'value' ); $original_translated_map = $this->get_original_translated_map_from_translation_memory( $strings_to_translate, $source_lang, $target_lang ); foreach ( $strings as &$string ) { $string->translated_from_memory = false; $string->translation = $string->value; if ( array_key_exists( $string->value, $original_translated_map ) ) { $string->translation = $original_translated_map[ $string->value ]; $string->translated_from_memory = true; } } return $strings; } /** * @param array $strings_to_translate * @param string $source_lang * @param string $target_lang * * @return array */ private function get_original_translated_map_from_translation_memory( $strings_to_translate, $source_lang, $target_lang ) { $args = array( 'strings' => $strings_to_translate, 'source_lang' => $source_lang, 'target_lang' => $target_lang, ); $strings_in_memory = apply_filters( 'wpml_st_get_translation_memory', array(), $args ); if ( $strings_in_memory ) { return wp_list_pluck( $strings_in_memory, 'translation', 'original' ); } return array(); } /** * Generate translation units. * * The units are the actual content to be translated * Represented as a source and a target * * @param stdClass $job * @param bool $apply_memory * * @return array The translation units data */ private function get_job_translation_units_data( $job, $apply_memory ) { $translation_units = array(); /** @var array $elements */ $elements = $job->elements; if ( $elements ) { $elements = $this->pre_populate_elements_with_translation_memory( $elements, $job->source_language_code, $job->language_code ); foreach ( $elements as $element ) { if ( 1 === (int) $element->field_translate ) { $field_data_translated = base64_decode( $element->field_data_translated ); $field_data = base64_decode( $element->field_data ); /** * It modifies the content of a single field data which represents, for example, one paragraph in post content. * * @since 2.10.0 * @param string $field_data */ $field_data = apply_filters( 'wpml_tm_xliff_unit_field_data', $field_data ); if ( 0 === strpos( $element->field_type, 'field-' ) ) { $field_data_translated = apply_filters( 'wpml_tm_xliff_export_translated_cf', $field_data_translated, $element ); $field_data = apply_filters( 'wpml_tm_xliff_export_original_cf', $field_data, $element ); } // check for untranslated fields and copy the original if required. if ( ! null === $field_data_translated || '' === $field_data_translated ) { $field_data_translated = $this->remove_invalid_chars( $field_data ); } if ( $this->is_valid_unit_content( $field_data ) ) { $translation_units[] = $this->get_translation_unit_data( $element->field_type, $element->field_type, $field_data, $apply_memory ? $field_data_translated : null, $apply_memory && $element->translated_from_memory, $element->field_wrap_tag, $this->get_field_title( $element, $job ) ); } } } } return $translation_units; } /** * @param \stdClass $field * @param \stdClass $job * * @return string */ private function get_field_title( $field, $job ) { $result = apply_filters( 'wpml_tm_adjust_translation_fields', [ (array) $field ], $job, null ); return Obj::pathOr( '', [ 0, 'title' ], $result ); } /** * @param array $elements * @param string $source_lang * @param string $target_lang * * @return array */ private function pre_populate_elements_with_translation_memory( array $elements, $source_lang, $target_lang ) { $strings_to_translate = array(); foreach ( $elements as &$element ) { if ( preg_match( '/^package-string/', $element->field_type ) ) { $strings_to_translate[ $element->tid ] = base64_decode( $element->field_data ); } $element->translated_from_memory = FieldId::is_any_term_field( $element->field_type ) && $element->field_data_translated && $element->field_data != $element->field_data_translated; } $original_translated_map = $this->get_original_translated_map_from_translation_memory( $strings_to_translate, $source_lang, $target_lang ); if ( $original_translated_map ) { foreach ( $elements as &$element ) { if ( array_key_exists( $element->tid, $strings_to_translate ) && array_key_exists( $strings_to_translate[ $element->tid ], $original_translated_map ) ) { $element->field_data_translated = base64_encode( $original_translated_map[ $strings_to_translate[ $element->tid ] ] ); $element->translated_from_memory = true; } } } return $elements; } /** * Get translation unit data. * * @param string $field_id Field ID. * @param string $field_name Field name. * @param string $field_data Field content. * @param string $field_data_translated Field translated content. * @param boolean $is_translated_from_memory Boolean flag - is translated from memory. * @param string $field_wrap_tag Field wrap tag (h1...h6, etc.) * @param string $title * * @return array */ private function get_translation_unit_data( $field_id, $field_name, $field_data, $field_data_translated, $is_translated_from_memory = false, $field_wrap_tag = '', $title = '' ) { global $sitepress; if ( $field_data === null ) { $field_data = ''; } if ( $field_data_translated === null ) { $field_data_translated = ''; } $field_data = $this->remove_invalid_chars( $field_data ); $translation_unit = array(); $field_data = $this->remove_line_breaks_inside_tags( $field_data ); $field_data_translated = $this->remove_line_breaks_inside_tags( $field_data_translated ); if ( $sitepress->get_setting( 'xliff_newlines' ) === WPML_XLIFF_TM_NEWLINES_REPLACE ) { $field_data = $this->replace_new_line_with_tag( $field_data ); $field_data_translated = $this->replace_new_line_with_tag( $field_data_translated ); } if ( $title ) { $translation_unit['attributes']['extradata'] = $title; } $translation_unit['attributes']['resname'] = $field_name; $translation_unit['attributes']['restype'] = 'string'; $translation_unit['attributes']['datatype'] = 'html'; $translation_unit['attributes']['id'] = $field_id; $translation_unit['source'] = array( 'content' => $field_data ); $translation_unit['target'] = array( 'content' => $field_data_translated ); $translation_unit['note'] = array( 'content' => $field_wrap_tag ); if ( $is_translated_from_memory ) { $translation_unit['target']['attributes'] = array( 'state' => 'needs-review-translation', 'state-qualifier' => 'tm-suggestion', ); } return $translation_unit; } /** * @param string $string * * @return string */ protected function replace_new_line_with_tag( $string ) { return str_replace( array( "\n", "\r" ), array( '<br class="xliff-newline" />', '' ), $string ); } private function remove_line_breaks_inside_tags( $string ) { return preg_replace_callback( '/(<[^>]*>)/m', array( $this, 'remove_line_breaks_inside_tag_callback' ), $string ); } /** * @param array $matches * * @return string */ private function remove_line_breaks_inside_tag_callback( array $matches ) { $tag_string = preg_replace( '/([\n\r\t ]+)/', ' ', $matches[0] ); $tag_string = preg_replace( '/(<[\s]+)/', '<', $tag_string ); return preg_replace( '/([\s]+>)/', '>', $tag_string ); } /** * @param string $string * * Remove all characters below 0x20 except for 0x09, 0x0A and 0x0D * @see https://www.w3.org/TR/xml/#charsets * * @return string */ private function remove_invalid_chars( $string ) { return preg_replace( '/[\x00-\x08\x0B-\x0C\x0E-\x1F]/', '', $string ); } /** * Save a xliff string to a temporary file and return the file ressource * handle * * @param string $xliff_content * * @return resource XLIFF */ private function generate_xliff_file( $xliff_content ) { $file = fopen( 'php://temp', 'rb+' ); if ( $file ) { fwrite( $file, $xliff_content ); rewind( $file ); } return $file; } /** * @param $job * * @return false|null|string */ private function get_external_url( $job ) { $external_file_url = null; if ( isset( $job->original_doc_id ) && 'post' === $job->element_type_prefix ) { $external_file_url = get_permalink( $job->original_doc_id ); return $external_file_url; } return $external_file_url; } /** * @param $content * * @return bool */ private function is_valid_unit_content( $content ) { $content = preg_replace( '/[^#\w]*/u', '', $content ); return $content || '0' === $content; } private function get_translator_notes( $job ) { $this->translator_notes = new WPML_TM_XLIFF_Translator_Notes( isset( $job->original_doc_id ) ? $job->original_doc_id : 0 ); } } class-wpml-tm-xliff-translator-notes.php 0000644 00000001001 14721654034 0014376 0 ustar 00 <?php /** * @author OnTheGo Systems */ class WPML_TM_XLIFF_Translator_Notes extends WPML_TM_XLIFF_Phase { private $post_id; public function __construct( $post_id = 0 ) { $this->post_id = $post_id; } /** * @return string */ protected function get_data() { if ( $this->post_id ) { return WPML_TM_Translator_Note::get( $this->post_id ); } else { return ''; } } protected function get_phase_name() { return 'notes'; } protected function get_process_name() { return 'Notes'; } } class-wpml-tm-xliff-shortcodes.php 0000644 00000002016 14721654034 0013243 0 ustar 00 <?php /** * @author OnTheGo Systems */ class WPML_TM_XLIFF_Shortcodes extends WPML_TM_XLIFF_Phase { const SHORTCODE_STORE_OPTION_KEY = 'wpml_xliff_shortcodes'; /** * @return string */ protected function get_data() { return implode( ',', $this->get_shortcodes() ); } /** * @return array */ private function get_shortcodes() { global $shortcode_tags; $registered_shortcodes = array(); if ( $shortcode_tags ) { $registered_shortcodes = array_keys( $shortcode_tags ); } $stored_shortcodes = get_option( self::SHORTCODE_STORE_OPTION_KEY, array() ); return $this->get_sanitized_shortcodes( $registered_shortcodes, $stored_shortcodes ); } private function get_sanitized_shortcodes( array $shortcodes1, array $shortcodes2 ) { return array_unique( array_filter( apply_filters( 'wpml_shortcode_list', array_merge( $shortcodes1, $shortcodes2 ) ) ) ); } protected function get_phase_name() { return 'shortcodes'; } protected function get_process_name() { return 'Shortcodes identification'; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0.18 |
proxy
|
phpinfo
|
Настройка