Index: branches/5.1.x/core/units/helpers/language_import_helper.php =================================================================== diff -u -N -r13086 -r13140 --- branches/5.1.x/core/units/helpers/language_import_helper.php (.../language_import_helper.php) (revision 13086) +++ branches/5.1.x/core/units/helpers/language_import_helper.php (.../language_import_helper.php) (revision 13140) @@ -1,6 +1,6 @@ node. There are +* two more nodes PHRASES and EVENTS for phrase and email event translations. +* +* v2 +* ========== +* All data, that will end up in Language table is now attributes of LANGUAGE node +* and is name exactly as field name, that will be used to store that data. +*/ + defined('FULL_PATH') or die('restricted access!'); define('LANG_OVERWRITE_EXISTING', 1); @@ -82,6 +96,13 @@ */ var $_debugMode = false; + /** + * Latest version of language pack format. Versions are not backwards compatible! + * + * @var int + */ + var $_latestVersion = 2; + function LanguageImportHelper() { parent::kHelper(); @@ -141,7 +162,7 @@ // copy data from temp tables to live foreach ($this->_languages as $language_id) { $this->_performUpgrade($language_id, 'phrases', 'PhraseKey'); - $this->_performUpgrade($language_id, 'emailmessages', 'EventId'); + $this->_performUpgrade_v2($language_id, 'emailevents', 'EventId', Array ('l%s_Subject', 'Headers', 'l%s_Body')); } $this->_initImportTables(true); @@ -175,22 +196,33 @@ $lang_table = $this->Application->getUnitOption('lang','TableName'); $phrases_table = $this->Application->getUnitOption('phrases','TableName'); - $emailevents_table = $this->Application->getUnitOption('emailmessages','TableName'); - $mainevents_table = $this->Application->getUnitOption('emailevents','TableName'); + $events_table = $this->Application->getUnitOption('emailevents','TableName'); $phrase_tpl = "\t\t\t".'%s'."\n"; $event_tpl = "\t\t\t".'%s'."\n"; $sql = 'SELECT * FROM %s WHERE LanguageId = %s'; - $ret = ''."\n"; + $ret = ''."\n"; + + $export_fields = $this->_getExportFields(); + + $email_message_helper =& $this->Application->recallObject('EmailMessageHelper'); + /* @var $email_message_helper EmailMessageHelper */ + foreach ($language_ids as $language_id) { // languages $row = $this->Conn->GetRow( sprintf($sql, $lang_table, $language_id) ); - $ret .= "\t".''.$row['DateFormat'].''; - $ret .= ''.$row['TimeFormat'].''.$row['InputDateFormat'].''; - $ret .= ''.$row['InputTimeFormat'].''.$row['DecimalPoint'].''; - $ret .= ''.$row['ThousandSep'].''.$row['Charset'].''.$row['UserDocsUrl'].''; - $ret .= ''.$row['UnitSystem'].''."\n"; + $ret .= "\t" . '_exportEncoding == 'base64' ? base64_encode($row['FilenameReplacements']) : ''; + $ret .= '' . "\n"; + // phrases $phrases_sql = 'SELECT * FROM '.$phrases_table.' WHERE LanguageId = %s AND PhraseType IN (%s) AND Module IN (%s) ORDER BY Phrase'; if( in_array('In-Portal',$module_ids) ) array_push($module_ids, ''); // for old language packs @@ -214,31 +246,29 @@ $module_sql = preg_replace('/(.*),/U', 'INSTR(Module,\'\\1\') OR ', implode(',', $module_ids).','); $module_sql = substr($module_sql, 0, -4); - $sql = 'SELECT EventId FROM '.$mainevents_table.' WHERE '.$module_sql; - $event_ids = $this->Conn->GetCol($sql); + $ret .= "\t\t".''."\n"; - if($event_ids) - { - $ret .= "\t\t".''."\n"; - $event_sql = ' SELECT em.* - FROM '.$emailevents_table.' em - LEFT JOIN '.$mainevents_table.' e ON e.EventId = em.EventId - WHERE em.LanguageId = %s AND em.EventId IN (%s) - ORDER BY e.Event, e.Type'; - $rows = $this->Conn->Query( sprintf($event_sql,$language_id, $event_ids ? implode(',',$event_ids) : '' ) ); - foreach($rows as $row) - { - if (!array_key_exists($row['EventId'], $this->events_hash)) { - // don't export existing translations of missing events - continue; - } + $event_sql = ' SELECT * + FROM ' . $events_table . ' + WHERE ' . $module_sql . ' + ORDER BY `Event`, `Type`'; + $rows = $this->Conn->Query($event_sql); - list($event_name, $event_type) = explode('_', $this->events_hash[ $row['EventId'] ] ); - $data = $this->_exportEncoding == 'base64' ? base64_encode($row['Template']) : ''; - $ret .= sprintf($event_tpl, $row['MessageType'], $event_name, $event_type, $data ); - } - $ret .= "\t\t".''."\n"; + foreach ($rows as $row) { + $fields_hash = Array ( + 'Headers' => $row['Headers'], + 'Subject' => $row['l' . $language_id . '_Subject'], + 'Body' => $row['l' . $language_id . '_Body'], + ); + + $template = $email_message_helper->buildTemplate($fields_hash); + + list($event_name, $event_type) = explode('_', $this->events_hash[ $row['EventId'] ] ); + $data = $this->_exportEncoding == 'base64' ? base64_encode($template) : ''; + $ret .= sprintf($event_tpl, $row['MessageType'], $event_name, $event_type, $data); } + $ret .= "\t\t".''."\n"; + $ret .= "\t".''."\n"; } @@ -268,7 +298,6 @@ */ function _performUpgrade($language_id, $prefix, $unique_field) { - // TODO: find a way to compare (intersect,diff) phrases in non-case sensitive way, but keeping original case in result $live_records = $this->_getTableData($language_id, $prefix, $unique_field, false); $temp_records = $this->_getTableData($language_id, $prefix, $unique_field, true); @@ -308,6 +337,77 @@ } /** + * Performs upgrade of given language pack part + * + * @param int $language_id + * @param string $prefix + * @param string $unique_field + * @param string $data_fields + */ + function _performUpgrade_v2($language_id, $prefix, $unique_field, $data_fields) + { + $live_records = $this->_getTableData_v2($language_id, $prefix, $unique_field, $data_fields[0], false); + $temp_records = $this->_getTableData_v2($language_id, $prefix, $unique_field, $data_fields[0], true); + + if (!$temp_records) { + // no data for given language + return ; + } + + // perform insert for records, that are missing in live table + $to_insert = array_diff($temp_records, $live_records); + + if ($to_insert) { + $to_insert = array_map(Array (&$this->Conn, 'qstr'), $to_insert); + + $sql = 'INSERT INTO ' . $this->Application->getUnitOption($prefix, 'TableName') . ' + SELECT * + FROM ' . $this->_tables[$prefix] . ' + WHERE ' . $unique_field . ' IN (' . implode(',', $to_insert) . ')'; + $this->Conn->Query($sql); + } + + // perform update for records, that are present in live table + $to_update = array_diff($temp_records, $to_insert); + + if ($to_update) { + $to_update = array_map(Array (&$this->Conn, 'qstr'), $to_update); + + $sql = 'UPDATE ' . $this->Application->getUnitOption($prefix, 'TableName') . ' live + SET '; + + foreach ($data_fields as $index => $data_field) { + $data_field = sprintf($data_field, $language_id); + + $sql .= ' live.' . $data_field . ' = ( + SELECT temp' . $index . '.' . $data_field . ' + FROM ' . $this->_tables[$prefix] . ' temp' . $index . ' + WHERE temp' . $index . '.' . $unique_field . ' = live.' . $unique_field . ' + ),'; + } + + $sql = substr($sql, 0, -1); // cut last comma + + $where_clause = Array ( + // this won't make any difference, but just in case + $unique_field . ' IN (' . implode(',', $to_update) . ')', + ); + + if ($this->import_mode == LANG_SKIP_EXISTING) { + // empty OR not set + $data_field = sprintf($data_fields[0], $language_id); + $where_clause[] = '(' . $data_field . ' = "") OR (' . $data_field . ' IS NULL)'; + } + + if ($where_clause) { + $sql .= "\n" . 'WHERE (' . implode(') AND (', $where_clause) . ')'; + } + + $this->Conn->Query($sql); + } + } + + /** * Returns data from given table used for language pack upgrade * * @param int $language_id @@ -330,6 +430,35 @@ return $this->Conn->GetCol($sql); } + /** + * Returns data from given table used for language pack upgrade + * + * @param int $language_id + * @param string $prefix + * @param string $unique_field + * @param bool $temp_mode + * @return Array + */ + function _getTableData_v2($language_id, $prefix, $unique_field, $data_field, $temp_mode = false) + { + $data_field = sprintf($data_field, $language_id); + $table_name = $this->Application->getUnitOption($prefix, 'TableName'); + + if ($temp_mode) { + // for temp table get only records, that have contents on given language (not empty and isset) + $sql = 'SELECT ' . $unique_field . ' + FROM ' . $this->Application->GetTempName($table_name, 'prefix:' . $prefix) . ' + WHERE (' . $data_field . ' <> "") AND (' . $data_field . ' IS NOT NULL)'; + } + else { + // for live table get all records, no matter on what language + $sql = 'SELECT ' . $unique_field . ' + FROM ' . $table_name; + } + + return $this->Conn->GetCol($sql); + } + function _parseXML($filename) { if ($this->_debugMode) { @@ -367,7 +496,7 @@ function _initImportTables($drop_only = false) { $this->_tables['phrases'] = $this->_prepareTempTable('phrases', $drop_only); - $this->_tables['emailmessages'] = $this->_prepareTempTable('emailmessages', $drop_only); + $this->_tables['emailevents'] = $this->_prepareTempTable('emailevents', $drop_only); } /** @@ -407,34 +536,76 @@ } /** + * Returns language fields to be exported + * + * @return Array + */ + function _getExportFields() + { + return Array ( + 'PackName', 'LocalName', 'DateFormat', 'TimeFormat', 'InputDateFormat', 'InputTimeFormat', + 'DecimalPoint', 'ThousandSep', 'Charset', 'UnitSystem', 'Locale', 'UserDocsUrl' + ); + } + + /** * Processes parsed XML * * @param kXMLNode $language_node */ function _processLanguages(&$language_node) { - $field_mapping = Array ( - 'DATEFORMAT' => 'DateFormat', - 'TIMEFORMAT' => 'TimeFormat', - 'INPUTDATEFORMAT' => 'InputDateFormat', - 'INPUTTIMEFORMAT' => 'InputTimeFormat', - 'DECIMAL' => 'DecimalPoint', - 'THOUSANDS' => 'ThousandSep', - 'CHARSET' => 'Charset', - 'UNITSYSTEM' => 'UnitSystem', - 'DOCS_URL' => 'UserDocsUrl', - ); + if (array_key_exists('VERSION', $language_node->Parent->Attributes)) { + // version present -> use it + $version = $language_node->Parent->Attributes['VERSION']; + } + else { + // version missing -> guess it + if (is_object($language_node->FindChild('DATEFORMAT'))) { + $version = 1; + } + elseif (array_key_exists('CHARSET', $language_node->Attributes)) { + $version = 2; + } + } + if ($version == 1) { + $field_mapping = Array ( + 'DATEFORMAT' => 'DateFormat', + 'TIMEFORMAT' => 'TimeFormat', + 'INPUTDATEFORMAT' => 'InputDateFormat', + 'INPUTTIMEFORMAT' => 'InputTimeFormat', + 'DECIMAL' => 'DecimalPoint', + 'THOUSANDS' => 'ThousandSep', + 'CHARSET' => 'Charset', + 'UNITSYSTEM' => 'UnitSystem', + 'DOCS_URL' => 'UserDocsUrl', + ); + } + else { + $export_fields = $this->_getExportFields(); + } + do { $language_id = false; $fields_hash = Array ( 'PackName' => $language_node->Attributes['PACKNAME'], 'LocalName' => $language_node->Attributes['PACKNAME'], 'Encoding' => $language_node->Attributes['ENCODING'], - 'Charset' => 'iso-8859-1', + 'Charset' => 'utf-8', ); + if ($version > 1) { + foreach ($export_fields as $export_field) { + $attribute_name = strtoupper($export_field); + + if (array_key_exists($attribute_name, $language_node->Attributes)) { + $fields_hash[$export_field] = $language_node->Attributes[$attribute_name]; + } + } + } + $sub_node =& $language_node->firstChild; /* @var $sub_node kXMLNode */ @@ -468,8 +639,21 @@ } break; + case 'REPLACEMENTS': + // added since v2 + $replacements = $sub_node->Data; + + if ($fields_hash['Encoding'] != 'plain') { + $replacements = base64_decode($replacements); + } + + $fields_hash['FilenameReplacements'] = $replacements; + break; + default: - $fields_hash[ $field_mapping[$sub_node->Name] ] = $sub_node->Data; + if ($version == 1) { + $fields_hash[ $field_mapping[$sub_node->Name] ] = $sub_node->Data; + } break; } } while (($sub_node =& $sub_node->NextSibling())); @@ -518,36 +702,45 @@ */ function _processEvents(&$event_node, $language_id, $language_encoding) { + static $events_added = Array (); + $email_message_helper =& $this->Application->recallObject('EmailMessageHelper'); /* @var $email_message_helper EmailMessageHelper */ do { $event_id = $this->_getEventId($event_node->Attributes['EVENT'], $event_node->Attributes['TYPE']); if ($event_id) { + if ($language_encoding == 'plain') { + $template = rtrim($event_node->Data); + } + else { + $template = base64_decode($event_node->Data); + } + + $parsed = $email_message_helper->parseTemplate($template); + $fields_hash = Array ( - 'LanguageId' => $language_id, 'EventId' => $event_id, + 'Event' => $event_node->Attributes['EVENT'], + 'Type' => $event_node->Attributes['TYPE'], 'MessageType' => $event_node->Attributes['MESSAGETYPE'], + 'l' . $language_id . '_Subject' => $parsed['Subject'], + 'l' . $language_id . '_Body' => $parsed['Body'], ); - if ($language_encoding == 'plain') { - $fields_hash['Template'] = rtrim($event_node->Data); + if ($parsed['Headers']) { + $fields_hash['Headers'] = $parsed['Headers']; } + + if (!in_array($event_id, $events_added)) { + $events_added[] = $event_id; + $this->Conn->doInsert($fields_hash, $this->_tables['emailevents']); + } else { - $fields_hash['Template'] = base64_decode($event_node->Data); + $this->Conn->doUpdate($fields_hash, $this->_tables['emailevents'], 'EventId = ' . $event_id); } - - $parsed = $email_message_helper->parseTemplate($fields_hash['Template']); - $fields_hash['Subject'] = $parsed['Subject']; - - $this->Conn->doInsert($fields_hash, $this->_tables['emailmessages'], 'INSERT', false); } } while (($event_node =& $event_node->NextSibling())); - - if ($fields_hash) { - // at least one corresponding event declaration found by email event name+type given in translation - $this->Conn->doInsert($fields_hash, $this->_tables['emailmessages'], 'INSERT'); - } } /**