* @author Gerry Weissbach */ /* * delete from tblTranslation Where Value like '%\?\?\?%' and Lang not in ('en', 'de', 'es'); * DROP TABLE IF EXISTS tblTranslation_old; * create table tblTranslation2 like tblTranslation; * INSERT INTO tblTranslation2 (KeyID, Lang, Date, Value, User) SELECT KeyID, Lang, MAX(Date), Value, User FROM tblTranslation GROUP BY Value, Lang, KeyID, User; * RENAME TABLE tblTranslation TO tblTranslation_old; * RENAME TABLE tblTranslation2 TO tblTranslation; */ // must be run within Dokuwiki if (!defined('DOKU_INC')) die(); if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); class helper_plugin_translator extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin var $database = null; var $checkedOK = null; var $pageWordLenIdx = null; var $revertableUpload = array(); var $dataBaseStruct = array ( "tblCategory" => array( 'statement' => "CREATE TABLE `tblCategory` ( `CategoryID` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY, `Name` VARCHAR( 255 ) NOT NULL , `FileName` VARCHAR( 255 ) NOT NULL ) CHARACTER SET utf8 COLLATE utf8_unicode_ci;", 'fields' => array('CategoryID', 'Name', 'FileName') ), "tblMasterKey" => array( 'statement' => "CREATE TABLE `tblMasterKey` ( `KeyID` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , `ResourceKey` VARCHAR( 255 ) NOT NULL , `Value` TEXT NOT NULL , `MD5KeyVal` VARCHAR( 255 ) NOT NULL , UNIQUE ( `Md5KeyVal` ) ) CHARACTER SET utf8 COLLATE utf8_unicode_ci;", 'fields' => array('KeyID', 'ResourceKey', 'Value', 'MD5KeyVal') ), "tblMaster" => array( 'statement' => "CREATE TABLE `tblMaster` ( `KeyID` INT( 11 ) NOT NULL, `CategoryID` INT( 11 ) NOT NULL, `Version` VARCHAR( 255 ) NOT NULL , PRIMARY KEY ( `KeyID`, `CategoryID`, `Version` ) ) CHARACTER SET utf8 COLLATE utf8_unicode_ci;", 'fields' => array('KeyID', 'CategoryID', 'Version') ), "tblTranslation" => array( 'statement' => "CREATE TABLE `tblTranslation` ( `KeyID` VARCHAR( 255 ) NOT NULL , `Lang` VARCHAR( 10 ) NOT NULL , `Date` DATETIME NOT NULL , `Value` TEXT NOT NULL , `User` VARCHAR( 255 ) NOT NULL , PRIMARY KEY ( `KeyID` , `Lang` , `Date` ) ) CHARACTER SET utf8 COLLATE utf8_unicode_ci;", 'fields' => array('KeyID', 'Lang', 'Value', 'User', 'Date') ), "tblUserRights" => array( 'statement' => "CREATE TABLE `tblUserRights` ( `User` VARCHAR( 255 ) NOT NULL , `Lang` VARCHAR( 255 ) NOT NULL , PRIMARY KEY ( `User` ) ) CHARACTER SET utf8 COLLATE utf8_unicode_ci;", 'fields' => array('User', 'Lang') ), ); function getInfo(){ return array_merge(confToHash(dirname(__FILE__).'/info.txt'), array( 'name' => 'Translator - Helper', )); } function getMethods() { $result = array(); return $result; } function checkDatabase() { if ( $this->init_database() === false ) { print "Cannot init."; return false; } if ( empty($this->database) ) { print $this->lang['nodatabase']; return false; } if ( !is_null($this->checkedOK) ) { return $this->checkedOK; } $this->checkedOK = $this->currentVersionChecked(); if ( $this->checkedOK ) { return $this->checkedOK; } foreach ($this->dataBaseStruct as $tableName => $sqlStatement ) { $this->database->query("SHOW TABLES LIKE \"$tableName\";"); //$this->database->execute($tableName); $allGood = false; if ( $this->database->num_rows() != 1 ) { msg("Creating table '$tableName'", 0); $this->database->prepare($sqlStatement['statement']); $this->database->execute(); $allGood = true; } else { // Check Log $table = $this->database->databaseConnection->escape_string($tableName); $this->database->prepare("SHOW COLUMNS FROM `$table`"); $this->database->execute(); if ( $this->database->num_rows() >= count($sqlStatement['fields']) ) { $allGood = true; $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { if ( !in_array($data['Field'], $sqlStatement['fields']) ) { msg("Field missing in '$tableName': '{$data['Field']}'", -1); $allGood = false; break; } } } } } $this->checkedOK = $allGood; if ( $allGood ) { $this->setCurrentVersionChecked(); } return $allGood; } function init_database() { if ( empty($this->database) ) { if ( !$this->database =& plugin_load('helper', 'databaseconnector', true) ) { return false;} $this->dbType = $dbType; $this->database->setType($this->getConf('DBType')); if ( $this->database->connect($this->getConf('DBName'), $this->getConf('DBUserName'), $this->getConf('DBUserPassword'), $this->getConf('DBHostName')) ) { return true; } return false; } } function close_database() { if ( !empty($this->database) ) { $this->database->close(); $this->database = null; } } function _hashKey($key, $value) { return md5($key . $value); } /* * Create Master Key / Value Entries */ function _createMasterTableEntry($translation, $categoryID, $version, &$finalStatus) { foreach ( $translation as $key => $value) { $KeyID = null; $HASHKEY = $this->_hashKey($key, $value); // Check if Key => Value is in tblMasterKeys $this->database->prepare("SELECT KeyID FROM tblMasterKey WHERE MD5KeyVal=?;"); $this->database->execute($HASHKEY); if ( $this->database->num_rows() == 0 ) { // Key is not in DB ... is there a Key with another value for the current Category? Order DESC by Version $this->database->prepare("SELECT tblMasterKey.KeyID FROM tblMaster LEFT JOIN tblMasterKey ON tblMaster.KeyID=tblMasterKey.KeyID WHERE ResourceKey=? AND CategoryID=? ORDER BY Version DESC;"); $this->database->execute($key, $categoryID); if ( $this->database->num_rows() > 0 ) { $data = array(); $this->database->bind_assoc($data); $this->database->fetch(); $KeyID = $data['KeyID']; // Update Database $this->database->prepare("UPDATE tblMasterKey SET Value=?, Md5KeyVal=? WHERE KeyID=?;"); $this->database->execute($value, $HASHKEY, $KeyID); } else { // The Key is not in the Database, so add it $this->database->prepare("INSERT INTO tblMasterKey (ResourceKey, Value, Md5KeyVal) VALUES(?, ?, ?);"); $this->database->execute($key, $value, $HASHKEY); $KeyID = $this->database->insert_id(); } } else { // Fetch inserted Key ID $data = array(); $this->database->bind_assoc($data); $this->database->fetch(); $KeyID = $data['KeyID']; // If exists with same Version Return $this->database->prepare("SELECT KeyID FROM tblMaster WHERE KeyID=? AND CategoryID=? AND Version=?;"); $this->database->execute($KeyID, $categoryID, $version); if ( $this->database->num_rows() > 0 ) { $finalStatus['KeysMatchingExisting']++; continue; } } // Add KeyID to tblMaster $this->database->prepare("INSERT INTO tblMaster (KeyID, CategoryID, Version) VALUES(?, ?, ?);"); $this->database->execute($KeyID, $categoryID, $version); continue; } } /* * get Category ID via Name */ function _getCategoryFromName($categoryName) { $CATEGORIES = $this->_getCategories($categoryName); if ( empty($CATEGORIES[$categoryName]) ) { msg($this->_messageReplacer('CategoryNotFound', $categoryName), -1); return false; } return array( $CATEGORIES[$categoryName]['CategoryID'], str_replace('[LANG]', '(.*?)', $CATEGORIES[$categoryName]['FileName'])); } /* * get Categories */ function _getCategories($categoryName=null, $isID=false) { $additional = ''; $execute = array(); $return = array(); if ( !empty($categoryName) ) { if ( $isID ) { $additional .= "WHERE CategoryID=?"; } else { $additional .= "WHERE Name=?"; } $execute[] = $categoryName; } $this->database->prepare("SELECT Name, CategoryID, FileName FROM tblCategory $additional;"); $this->database->execute($execute); if ( $this->database->num_rows() == 0 ) { return $return; } $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $return[$data['Name']] = array( 'CategoryID' => $data['CategoryID'], 'FileName' => $data['FileName'], ); } return $return; } /* * Get Available Languages for a Category */ function _getAvailableLanguagesVersions($categoryID) { if ( !$categoryID ) { return; } $SQL = "Select DISTINCT Version, Lang FROM (SELECT Version, keyID FROM tblMaster WHERE CategoryID=? GROUP BY Version) as t1 INNER JOIN tblTranslation ON t1.KeyID = tblTranslation.KeyID"; $SQL = "SELECT Version, Lang FROM tblTranslation INNER JOIN tblMaster ON tblMaster.KeyID=tblTranslation.KeyID WHERE CategoryID=? GROUP BY Version, Lang;"; // Approximativ - vermutlich zu viel, aber besser als gar nix! $SQL = "SELECT Lang, Version FROM (SELECT DISTINCT Lang FROM tblTranslation) as t1 CROSS JOIN (SELECT DISTINCT Version FROM tblMaster WHERE CategoryID=?) as t2;"; $this->database->prepare($SQL); $this->database->execute($categoryID); $languages = array(); $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $languages[$data['Version']][] = $data['Lang']; } foreach ( array_keys($languages) as $key ) { array_unshift($languages[$key], $this->getConf('default_language')); } return $languages; } /* * Get Available Languages for a Category */ function _getAvailableLanguages($categoryID, $version=null) { $EXECUTE = array($categoryID); if ( !empty($version) ) { $ADDITIONAL .= " AND Version=?"; $EXECUTE[] = $version; } $languages = array($this->getConf('default_language')); $this->database->prepare("SELECT Lang FROM tblTranslation INNER JOIN tblMaster ON tblMaster.KeyID=tblTranslation.KeyID WHERE CategoryID=? $ADDITIONAL GROUP BY Lang;"); $this->database->execute($EXECUTE); $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $languages[] = $data['Lang']; } return $languages; } /* * Get Available Versions for a Category */ function _getAvailableVersions($categoryID=null) { $EXECUTE = array(); $ADDITIONAL = ""; if ( !empty( $categoryID) ) { $EXECUTE[] = $categoryID; $ADDITIONAL .= " WHERE CategoryID=? "; } $versions = array(); $this->database->prepare("SELECT Version FROM tblMaster $ADDITIONAL GROUP BY Version;"); $this->database->execute($EXECUTE); $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $versions[] = trim($data['Version']); } $versions = array_unique($versions); usort($versions, array($this, "_sortVersion")); return $versions; } function _sortVersion($a, $b) { if ($a === $b ) { return 0; } else if ( is_numeric($a) && is_numeric($b) ) { return $a < $b ? -1 : 1; } else if ( !is_numeric($a) && !is_numeric($b) ) { return strcmp($a, $b); } else if ( !is_numeric($a)) { return 1; } else { return -1; } } /* * Get Key from Master via Name */ function _getMasterKeyIDFromName($KeyName, $CategoryID, $isID=false, $getID=true) { $this->setUTF8Collation(); $WHATTO = 'tblMasterKey.ResourceKey'; if ( $isID ) { $WHATTO = 'tblMasterKey.KeyID'; } // Get latest MasterKeyID from Category $this->database->prepare("SELECT DISTINCT tblMasterKey.KeyID, Value FROM tblMasterKey INNER JOIN tblMaster ON tblMasterKey.KeyID=tblMaster.KeyID WHERE tblMaster.CategoryID=? AND $WHATTO=? ORDER BY Version DESC;"); $this->database->execute($CategoryID, $KeyName); if ( $this->database->num_rows() > 0 ) { $data = array(); $this->database->bind_assoc($data); $this->database->fetch(); return $getID ? $data['KeyID'] : $data['Value']; } else { return false; } } /** * Check for all Keys if they do have a master - otherwise, kick them * @param $translation * @param $finalStatus * @param $CategoryID */ function _checkForMasterKeyIDs(&$translation, &$finalStatus, $CategoryID) { $this->setUTF8Collation(); $keysIN = implode("\",\"", array_keys($translation)); // $keysIN = str_replace("(", "\(", $keysIN); // $keysIN = str_replace(")", "\)", $keysIN); // Get latest MasterKeyID from Category $SQL = "SELECT DISTINCT tblMasterKey.KeyID, tblMasterKey.ResourceKey FROM tblMasterKey INNER JOIN tblMaster ON tblMasterKey.KeyID=tblMaster.KeyID WHERE tblMaster.CategoryID=? AND tblMasterKey.ResourceKey in ( \"$keysIN\" ) ORDER BY Version DESC;"; $this->database->prepare($SQL); $this->database->execute($CategoryID); $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $translation[$data['ResourceKey']] = array( 'ID' => $data['KeyID'], 'Value' => $translation[$data['ResourceKey']] ); $finalStatus['CountOfKeys']++; } } /* * Check if a key exists - additionally check for Lang and Value */ function _keyExists($KeyID, $Lang=null, $Value=null, $Date=null) { $additional = ""; $execute = array( $KeyID ); if ( !empty($Lang) ) { $additional .= " AND Lang=?"; $execute[] = $Lang; } if ( !empty($Date) ) { $additional .= " AND Date=?"; $execute[] = $Date; } $SQL = "SELECT KeyID, Value FROM tblTranslation WHERE KeyID=? $additional"; // Checks for the latest Entry if ( !empty($Value) ) { $SQL = "SELECT tblTrans.KeyID FROM ($SQL ORDER BY Date DESC LIMIT 1) as tblTrans WHERE BINARY tblTrans.Value=?"; $execute[] = $Value; } $this->database->prepare("$SQL;"); $this->database->execute($execute); if ( $this->database->num_rows() == 0 ) { return false; } return true; } /* * Insert a new Translation Set of Translations */ function _insertTranslation($KeyID, $Value, $Lang, $Date, $User=null) { global $INFO; $revertable = false; if ( empty($Value) ) return $revertable; // Bottleneck 2 if ( $this->_keyExists($KeyID, $Lang) ) { if ( $this->_keyExists($KeyID, $Lang, $Value) || $this->_keyExists($KeyID, $Lang, null, $Date) ) { return -1; } $revertable = true; } // Insert Translation if ( empty($User) ) $User = $_SERVER['REMOTE_USER']; $this->database->prepare("INSERT INTO tblTranslation (KeyID, Lang, Value, User, Date) VALUES(?, ?, ?, ?, ?);"); $this->database->execute($KeyID, $Lang, $Value, $User, $Date); return $revertable; } function _insertTranslationArray($translation, &$finalStatus, $Lang, $Date) { $CHECK = ''; $translationINV = array(); foreach( $translation as $key => $value ) { if ( !is_array($value) ) { $finalStatus['KeysNotInMaster'][] = $key; unset($translation[$key]); continue; } $CHECK .= $value['ID'] . ","; $translationINV[$value['ID']] = array( 'key' => $key, 'value' => $value['Value'] ); } $CHECK = substr($CHECK, 0, -1); $SQL = "SELECT DISTINCT * FROM (SELECT KeyID, Value, Date FROM tblTranslation WHERE KeyID in ( $CHECK ) AND Lang=? ORDER BY Date) as t1 GROUP BY KeyID;"; $this->database->prepare($SQL); $this->database->execute($Lang); $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { if ( $translationINV[$data['KeyID']]['value'] == $data['Value'] || $data['Date'] == $Date ) { $finalStatus['KeysMatchingExisting'][] = $translationINV[$data['KeyID']]['key']; unset( $translation[$translationINV[$data['KeyID']]['key']] ); unset( $translationINV[$data['KeyID']] ); continue; } $finalStatus['CountOfToBeReverted']++; } unset($SQL); if ( empty($translation) ) { return; } $INSERT = ''; $INSERTDATA = array(); foreach( $translation as $key => $value ) { $INSERTDATA[] = $value['ID']; $INSERTDATA[] = $Lang; $INSERTDATA[] = $value['value']; $INSERTDATA[] = $_SERVER['REMOTE_USER']; $INSERTDATA[] = $Date; $INSERT .= '(?,?,?,?,?),'; } $INSERT = substr($INSERT, 0, -1); $this->database->prepare("INSERT INTO tblTranslation (KeyID, Lang, Value, User, Date) VALUES $INSERT;"); $this->database->execute($INSERTDATA); } function _getTranslation($CategoryID, $Version, $Language) { $this->setUTF8Collation(); if ( $Language == $this->getConf('default_language') ) { $this->database->prepare("SELECT ResourceKey, Value FROM tblMasterKey INNER JOIN tblMaster ON tblMasterKey.KeyID=tblMaster.KeyID WHERE tblMaster.Version=? AND tblMaster.CategoryID=? ORDER BY tblMasterKey.ResourceKey;"); $this->database->execute($Version, $CategoryID); } else { $this->database->prepare("SELECT tblMasterKey.ResourceKey, tblTrans.Value FROM (tblMasterKey INNER JOIN tblMaster ON tblMasterKey.KeyID=tblMaster.KeyID AND tblMaster.Version=? AND tblMaster.CategoryID=?) INNER JOIN (SELECT * FROM (SELECT * FROM tblTranslation WHERE tblTranslation.Lang=? ORDER BY tblTranslation.Date DESC) as tblTemp1 GROUP BY KeyID) as tblTrans ON tblMasterKey.KeyID = tblTrans.KeyID ORDER BY ResourceKey, Value;"); $this->database->execute($Version, $CategoryID, $Language); } $return = array(); $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $return[$data['ResourceKey']] = $data['Value']; } return $return; } /* * Revert Entries by dateTime, lang and user */ function _revertEntries($dateTime, $lang, $user=null) { global $INFO; if ( empty($user) ) $user = $_SERVER['REMOTE_USER']; $this->database->prepare("DELETE FROM tblTranslation WHERE Date=? AND Lang=? AND User=?;"); $this->database->execute($dateTime, $lang, $user); return $this->database->num_rows(); } /* ****************************************************************************************************************************** * Export Manager ***************************************************************************************************************************** */ function _exportManager($caller) { $form = $caller->_startFieldSet($this->getLang('ExportManager'), 'translator_export'); list($categories, $changeCategory, $changeVersion) = $this->_getChangerJS(); $versions = array(); $languages = array(); if ( is_array($categories[$_REQUEST['Category']]['Versions']) ) { $versions = array_keys($categories[$_REQUEST['Category']]['Versions']); } if ( is_array($categories[$_REQUEST['Category']]['Versions'][$_REQUEST['Version']]) ) { $languages = array_values($categories[$_REQUEST['Category']]['Versions'][$_REQUEST['Version']]); } $form->addElement(form_makeListboxField('Category', array_keys($categories), $_REQUEST['Category'], $this->getLang('Category') . ':', null, null, array('onchange' => $changeCategory))); $form->addElement(form_makeTag('br')); $form->addElement(form_makeListboxField('Version', $versions, $_REQUEST['Version'], $this->getLang('Version') . ':', null, null, array('onchange' => $changeVersion))); $form->addElement(form_makeTag('br')); $form->addElement(form_makeListboxField('Language', $languages, $_REQUEST['Language'], $this->getLang('Language') . ':')); $form->addElement(form_makeTag('br')); $form->addElement(form_makeField('submit', 'export_language', $this->getLang('Export'), '')); $caller->_finishFieldset($form); } /** * Set headers and send the file to the client */ function _sendLanguageFile($data, $fileName){ global $conf; // send headers header("Content-Type: text"); // nocache header('Cache-Control: must-revalidate, no-transform, post-check=0, pre-check=0'); header('Pragma: public'); header('Content-Disposition: attachment; filename="'.$fileName.'";'); // send file contents if ( !empty($data) ) { foreach( $data as $line ) { print "$line\r\n"; flush(); } } } function capture_form_output($form) { if ( !$form ) { return ''; } ob_start(); $form->printForm(); $output = ob_get_contents(); ob_end_clean(); return trim($output); } function _getTranslationMatrix($category, $Version, $Language, $additionalLanguage=null, $start="0", $amount="50", $display=null, $filter=null) { $this->setUTF8Collation(); if ( empty($display) ) { $display = $this->getLang('notTranslated'); } $matrix = array(); list($categoryID) = $this->_getCategoryFromName($category); if ( $additionalLanguage == $this->getConf('default_language') ) $additionalLanguage = null; $ADDITIONAL = ""; $ADDITIONALKEYS = ""; $EXECUTE = array($categoryID, $Version, $Language, $Language); // add SQL for Additional Language if ( !empty($additionalLanguage) ) { $ADDITIONAL .= "LEFT JOIN ( tblTranslation as AdditionalTranslation INNER JOIN (SELECT KeyID, MAX(Date) AS Date FROM tblTranslation WHERE Lang=? GROUP BY KeyID) as tblOldestAddTrans ON tblOldestAddTrans.KeyID=AdditionalTranslation.KeyID AND tblOldestAddTrans.Date=AdditionalTranslation.Date AND AdditionalTranslation.Lang=?) ON tblMaster.KeyID = AdditionalTranslation.KeyID "; $ADDITIONALKEYS .= ", AdditionalTranslation.Value as AdditionalValue"; array_push($EXECUTE, $additionalLanguage); array_push($EXECUTE, $additionalLanguage); } // Modify Display settings switch($display) { case $this->getLang('notTranslated') : $ADDITIONAL .= "WHERE tblTranslation.Value IS NULL"; break; case $this->getLang('translated') : $ADDITIONAL .= "WHERE tblTranslation.Value IS NOT NULL"; break; } // Add Filter if ( !empty($filter) ) { if ( !strstr($ADDITIONAL, 'WHERE')) { $ADDITIONAL .= "WHERE"; } else { $ADDITIONAL .= " AND"; } if( substr($filter, 0, 1) != "%" ) $filter = "%" . $filter; if( substr($filter, -1) != "%" ) $filter .= "%"; $ADDITIONAL .= ' (ResourceKey LIKE ? OR tblMasterKey.Value LIKE ? OR tblTranslation.Value LIKE ?'; if ( !empty($additionalLanguage) ) { $ADDITIONAL .= ' OR AdditionalTranslation.Value LIKE ? '; array_push($EXECUTE, $filter); } $ADDITIONAL .= ')'; array_push($EXECUTE, $filter); array_push($EXECUTE, $filter); array_push($EXECUTE, $filter); } if ( empty($amount) ) $amount = 50; // Limits // Build SQL $SQL = "SELECT tblMasterKey.KeyID, ResourceKey, tblMasterKey.Value as MasterValue, tblTranslation.Value as TranslationValue, tblTranslation.User $ADDITIONALKEYS FROM ( ( tblMasterKey INNER JOIN tblMaster ON tblMaster.KeyID = tblMasterKey.KeyID AND CategoryID = ? AND Version = ? ) LEFT JOIN ( tblTranslation INNER JOIN (SELECT KeyID, MAX(Date) AS Date FROM tblTranslation WHERE Lang=? GROUP BY KeyID) as tblOldestTrans ON tblOldestTrans.KeyID=tblTranslation.KeyID AND tblOldestTrans.Date=tblTranslation.Date ) ON tblMaster.KeyID = tblTranslation.KeyID AND Lang=? ) $ADDITIONAL ORDER BY tblMaster.KeyID LIMIT ? OFFSET ?;"; $this->database->prepare("$SQL"); $this->database->execute(array_merge($EXECUTE, array('1000000000000', '0'))); $max = $this->database->num_rows(); array_push($EXECUTE, intval($amount)); array_push($EXECUTE, intval($start)); // Fetch Data $this->database->execute($EXECUTE); if ( $this->database->num_rows() == 0 ) { return false; } $data = array(); $this->database->bind_assoc($data); while ( $this->database->fetch() ) { $matrix[$data['KeyID']] = array ( 'KeyID' => $data['KeyID'], 'ResourceKey' => $data['ResourceKey'], 'MasterValue' => str_replace('\n', "\r\n", $this->_stringDecode($data['MasterValue'])), 'TranslationValue' => str_replace('\n', "\r\n", $this->_stringDecode($data['TranslationValue'])), 'AdditionalValue' => str_replace('\n', "\r\n", $this->_stringDecode($data['AdditionalValue'])), 'User' => $data['User'], ); } return array($matrix, $max); } /* * get the users languages from the database and return false if none are there */ function _getLanguages() { global $INFO; // print_r($INFO); // print $_SERVER['REMOTE_USER']; if ( empty($INFO['userinfo']) ) { return false; } $this->database->prepare("SELECT Lang FROM tblUserRights WHERE User=?"); $this->database->execute($_SERVER['REMOTE_USER']); if ( $this->database->num_rows() == 0) { return false; } $data = array(); $this->database->bind_assoc($data); $this->database->fetch(); return trim($data['Lang']); } function _handleRequest($fn) { if (is_array($fn)) { $cmd = key($fn); $param = is_array($fn[$cmd]) ? key($fn[$cmd]) : null; } else { $cmd = $fn; $param = null; } switch($cmd){ case "revertuploaded" : $this->_revertUploadedEntries($_REQUEST['revert']); break; case "translate" : $this->_updateTranslation($_REQUEST['Language'], $_REQUEST['translation']); break; case "setpage" : $_REQUEST['start'] = $param; $_REQUEST['amount'] = $_REQUEST['newAmount']; $_REQUEST['AdditionalLanguage'] = $_REQUEST['newAdditionalLanguage']; break; case "setup" : $_REQUEST['Language'] = $param; $_REQUEST['Display'] = $param ? key($fn[$cmd][$param]) : null; break; } } function _stringEncode($var) { // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT $ascii = ''; //$var = str_replace("\n", '\n', $var); $strlen_var = strlen($var); /* * Iterate over every character in the string, * escaping with a slash or encoding to UTF-8 where necessary */ for ($c = 0; $c < $strlen_var; ++$c) { $ord_var_c = ord($var{$c}); switch ($ord_var_c) { case 0x08: $ascii .= '\b'; break; case 0x09: $ascii .= '\t'; break; case 0x0A: $ascii .= '\n'; break; case 0x0C: $ascii .= '\f'; break; case 0x0D: $ascii .= '\r'; break; case 0x3D: $ascii .= '\='; break; case 0x3A: $ascii .= '\:'; break; case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): // characters U-00000000 - U-0000007F (same as ASCII) $ascii .= $var{$c}; break; case (($ord_var_c & 0xE0) == 0xC0): // characters U-00000080 - U-000007FF, mask 110XXXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c+1})); $c+=1; //$utf16 = mb_convert_encoding($char, 'UTF-16', 'UTF-8'); $utf16 = utf8_to_utf16be($char); $ascii .= sprintf('\u%04s', strtoupper(bin2hex($utf16))); break; case (($ord_var_c & 0xF0) == 0xE0): // characters U-00000800 - U-0000FFFF, mask 1110XXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c+1}), ord($var{$c+2})); $c+=2; //$utf16 = mb_convert_encoding($char, 'UTF-16', 'UTF-8'); $utf16 = utf8_to_utf16be($char); $ascii .= sprintf('\u%04s', strtoupper(bin2hex($utf16))); break; case (($ord_var_c & 0xF8) == 0xF0): // characters U-00010000 - U-001FFFFF, mask 11110XXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c+1}), ord($var{$c+2}), ord($var{$c+3})); $c+=3; //$utf16 = mb_convert_encoding($char, 'UTF-16', 'UTF-8'); $utf16 = utf8_to_utf16be($char); $ascii .= sprintf('\u%04s', strtoupper(bin2hex($utf16))); break; case (($ord_var_c & 0xFC) == 0xF8): // characters U-00200000 - U-03FFFFFF, mask 111110XX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c+1}), ord($var{$c+2}), ord($var{$c+3}), ord($var{$c+4})); $c+=4; //$utf16 = mb_convert_encoding($char, 'UTF-16', 'UTF-8'); $utf16 = utf8_to_utf16be($char); $ascii .= sprintf('\u%04s', strtoupper(bin2hex($utf16))); break; case (($ord_var_c & 0xFE) == 0xFC): // characters U-04000000 - U-7FFFFFFF, mask 1111110X // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c+1}), ord($var{$c+2}), ord($var{$c+3}), ord($var{$c+4}), ord($var{$c+5})); $c+=5; //$utf16 = mb_convert_encoding($char, 'UTF-16', 'UTF-8'); $utf16 = utf8_to_utf16be($char); $ascii .= sprintf('\u%04s', strtoupper(bin2hex($utf16))); break; } } return $ascii; } function _stringDecode($chrs) { // STRINGS RETURNED IN UTF-8 FORMAT $utf8 = ''; $chrs = addcslashes(trim($chrs), "\n\t\r"); // Ensure to have the Line Breaks intact $strlen_chrs = strlen($chrs); for ($c = 0; $c < $strlen_chrs; ++$c) { $substr_chrs_c_2 = substr($chrs, $c, 2); $ord_chrs_c = ord($chrs{$c}); switch ($substr_chrs_c_2) { case '\b': $utf8 .= chr(0x08); $c+=1; break; case '\t': $utf8 .= chr(0x09); $c+=1; break; case '\n': $utf8 .= chr(0x0A); $c+=1; break; case '\f': $utf8 .= chr(0x0C); $c+=1; break; case '\r': $utf8 .= chr(0x0D); $c+=1; break; case '\:': $utf8 .= chr(0x3A); $c+=1; break; case '\=': $utf8 .= chr(0x3D); $c+=1; break; default: if (preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6))) { // single, escaped unicode character $utf16 = chr(hexdec(substr($chrs, ($c+2), 2))) . chr(hexdec(substr($chrs, ($c+4), 2))); //$utf8 .= mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); // This is correct for rusians $utf8 .= utf16be_to_utf8($utf16); // this as Well; //$utf8 .= utf8_decode(utf16be_to_utf8($utf16)); $c+=5; } elseif(($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F)) { $utf8 .= $chrs{$c}; } elseif(($ord_chrs_c & 0xE0) == 0xC0) { // characters U-00000080 - U-000007FF, mask 110XXXXX //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= substr($chrs, $c, 2); $c += 1; } elseif(($ord_chrs_c & 0xF0) == 0xE0) { // characters U-00000800 - U-0000FFFF, mask 1110XXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= substr($chrs, $c, 3); $c += 2; } elseif(($ord_chrs_c & 0xF8) == 0xF0) { // characters U-00010000 - U-001FFFFF, mask 11110XXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= substr($chrs, $c, 4); $c += 3; } elseif(($ord_chrs_c & 0xFC) == 0xF8) { // characters U-00200000 - U-03FFFFFF, mask 111110XX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= substr($chrs, $c, 5); $c += 4; } elseif(($ord_chrs_c & 0xFE) == 0xFC) { // characters U-04000000 - U-7FFFFFFF, mask 1111110X // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= substr($chrs, $c, 6); $c += 5; } else { // Falls doch noch andere UTF8 Zeichen enthalten sind, mŸssen die extra behandelt werden. $utf8 .= chr($ord_chrs_c); } break; } } // print " '$utf8' "; return $utf8; } function _revertManager($parent, $revertableUpload) { // If there is nothing revertable then end here; if ( count($revertableUpload) == 0 ) { return; } $form = $parent->_startFieldSet($this->getLang('RevertManager'), 'translator_revert'); if ( count($revertableUpload) > 1 ) { foreach ( $revertableUpload as $revertable ) { $form->addElement(form_makeCheckboxField("revert[{$revertable['current_date_time']}][{$revertable['lang']}]", 1, $revertable['name'] . " ({$revertable['lang']})", null)); $form->addElement(form_makeTag('br')); } } else { $form->addElement($this->getLang('RevertManagerHowTo')); $revertable = array_shift($revertableUpload); $form->addHidden("revert[{$revertable['current_date_time']}][{$revertable['lang']}]", 1); } $form->addElement(form_makeField('submit', 'fn[revertuploaded]', $this->getLang('Revert'), '')); return $parent->_finishFieldset($form); } function _updateTranslation($Language, $Translation) { global $INFO; $date = date('Y-m-d H:i:s'); $this->revertableUpload = array(); if ( !in_array($Language, explode('|', $this->_getLanguages()) ) ) return; $finalStatus = array( 'KeysNotInMaster' => array(), 'KeysMatchingExisting' => array(), 'CountOfKeys' => 0, 'CountOfToBeReverted' => 0, ); foreach( $Translation as $key => $value ) { $finalStatus['CountOfKeys']++; if ( empty($value) ) { $finalStatus['RemovedUserKeys'] += $this->_tryRevertEntry($Language, $_SERVER['REMOTE_USER'], $key); continue; } // 18.05.2010 // Entfernt, damit Russische †bersetzungen wieder funktionieren // $value = utf8_decode($value); // 20.05.2010 // Russisch darf nicht ... aber Detusch z.B. muss ... welche Ausnahmen gibt es noch? //if ( !in_array( $Language, array("ru", "zh_CN", "zh_TW", "ja", "ko") ) ) { // $value = utf8_decode($value); //} // 06.12.2010 - €nderung der Datenbank. Es werden nur noch die codierten Werte gespeichert. $value = $this->_stringEncode($value); $status = $this->_insertTranslation($key, $value, $Language, $date); if ( $status === -1 ) { $finalStatus['KeysMatchingExisting'][]=$key; } else if ( $status === true ) { $finalStatus['CountOfToBeReverted']++; } } if ( $finalStatus['CountOfToBeReverted'] > 0 ) { $this->revertableUpload[$date] = array( 'lang' => $Language, 'name' => $_SERVER['REMOTE_USER'], 'current_date_time' => $date, ); } $this->_statusResult($finalStatus, $Language); return; } /* * Revert Uploaded Entries */ function _revertUploadedEntries($revertItems) { if ( !is_array($revertItems) ) return; foreach ( array_keys($revertItems) as $revertDateTime ) { foreach ( array_keys($revertItems[$revertDateTime]) as $lang ) { $items = $this->_revertEntries($revertDateTime, $lang); msg($this->_messageReplacer('RevertedItems', $items)); } } } function _tryRevertEntry($language, $user, $key=null) { if ( $language == $this->getConf('defaultLanguage') ) return false; $ADDITIONAL = ""; $EXECUTE = array($user, $language); if ( !empty( $key )) { $SQL = "SELECT tblTranslation.* FROM ((SELECT KeyID, MAX(Date) AS Date FROM tblTranslation WHERE Lang=? GROUP BY KeyID) as tblOldestAddTrans INNER JOIN tblTranslation ON tblOldestAddTrans.KeyID=tblTranslation.KeyID AND tblOldestAddTrans.Date=tblTranslation.Date) WHERE tblTranslation.KeyID=? AND tblTranslation.User=?;"; $this->database->prepare($SQL); $this->database->execute($language, $key, $user); $data = array(); $this->database->bind_assoc($data); $this->database->fetch(); $ADDITIONAL .= " AND KeyID=? AND Date=?"; $EXECUTE[] = $data['KeyID']; $EXECUTE[] = $data['Date']; } $this->database->prepare("DELETE FROM tblTranslation WHERE User=? AND Lang=? $ADDITIONAL"); $this->database->execute($EXECUTE); return $this->database->num_rows(); } function _statusResult($finalStatus, $Lang, $FileName="User created") { if ( !is_array($finalStatus['KeysNotInMaster']) ) $finalStatus['KeysNotInMaster'] = array($finalStatus['KeysNotInMaster']); if ( !is_array($finalStatus['KeysMatchingExisting']) ) $finalStatus['KeysMatchingExisting'] = array($finalStatus['KeysMatchingExisting']); if ( count($finalStatus['KeysNotInMaster']) > 0 ) { msg($this->_messageReplacer('KeysNotInMaster', array(count($finalStatus['KeysNotInMaster']), count($finalStatus['KeysNotInMaster'])+$finalStatus['CountOfKeys'], $Lang, $FileName, implode(', ', $finalStatus['KeysNotInMaster']))), -1); } if ( count($finalStatus['KeysMatchingExisting']) > 0 ) { msg($this->_messageReplacer('KeysMatchingExisting', array(count($finalStatus['KeysMatchingExisting']), $finalStatus['CountOfKeys'], $Lang, $FileName, implode(', ', $finalStatus['KeysMatchingExisting']))), 1); } if ( $finalStatus['RemovedUserKeys'] > 0 ) { msg($this->_messageReplacer('RemovedUserKeys', array($finalStatus['RemovedUserKeys'], $finalStatus['CountOfKeys']))); } if ( $finalStatus['CountOfToBeReverted'] > 0 ) { msg($this->getLang('RevertValues')); return false; } if ( $finalStatus['KeysMatchingExisting'] == $finalStatus['CountOfKeys'] || ( $finalStatus['KeysMatchingExisting'] == 0 && $finalStatus['CountOfToBeReverted'] == 0 && $finalStatus['KeysNotInMaster'] == 0 ) ) { $_REQUEST['start'] += $_REQUEST['amount']; } return true; } function _getAmountOfMasterValues($categoryID=null, $version=null) { $EXECUTE = array(); $ADDITIONAL = ''; if ( !empty($categoryID) ) { $ADDITIONAL .= "CategoryID=?"; $EXECUTE[] = $categoryID; } if ( !empty($categoryID) ) { if ( !empty($ADDITIONAL) ) { $ADDITIONAL .= " AND "; } $ADDITIONAL .= "Version=?"; $EXECUTE[] = $version; } if ( !empty($ADDITIONAL) ) { $ADDITIONAL = " WHERE " . $ADDITIONAL; } $this->database->prepare("SELECT COUNT(tblMaster.KeyID) as amount FROM tblMasterKey INNER JOIN tblMaster ON tblMasterKey.KeyID=tblMaster.KeyID $ADDITIONAL;"); $this->database->execute($EXECUTE); $data=array(); $this->database->bind_assoc($data); $this->database->fetch(); return intval($data['amount']); } function _getAmountOfLanguageValues($categoryID, $version, $lang) { //$SQL = "SELECT COUNT(DISTINCT tblMaster.KeyID) as amount FROM tblMaster INNER JOIN tblTranslation ON tblMaster.KeyID=tblTranslation.KeyID WHERE Lang=? AND categoryID=? and Version=?;"; $SQL = "SELECT COUNT(DISTINCT tblMaster.KeyID) as amount FROM tblMaster INNER JOIN tblTranslation ON tblMaster.KeyID=tblTranslation.KeyID AND Lang=? WHERE categoryID=? and Version=?;"; //$SQL = "SELECT COUNT(tblMaster.KeyID) as amount FROM tblMaster INNER JOIN (SELECT KeyID FROM tblTranslation WHERE Lang=? GROUP BY KeyID) as tblTrans ON tblTrans.KeyID=tblMaster.KeyID WHERE categoryID=? and Version=?;"; $this->database->prepare($SQL); $this->database->execute($lang, $categoryID, $version); $data=array(); $this->database->bind_assoc($data); $this->database->fetch(); return intval($data['amount']); } function _downloadLink($category, $version, $language, $name, $NoEncoded=false) { global $ID; $options = array('export_language' => '1', 'Category' => $category, 'Version' => $version, 'Language' => $language); if ( $NoEncoded ) $options['NoEncode'] = 'true'; return tpl_link(wl($ID, $options),$name,null,true); } function _messageReplacer($LangKey, $replacements) { if ( !is_array($replacements)) $replacements = array($replacements); $language = $this->getLang($LangKey); for($i=0; $i_getCategories(); $JSCategories = "var categories = {"; foreach ( $categories as $name => $category ) { // Bottlenec 1 $languages = $this->_getAvailableLanguagesVersions($category['CategoryID']); $versions = array_keys($languages); rsort($versions, SORT_NUMERIC); if ( empty($versions) ) { continue; } $JSCategories .= "'$name': {"; // Bottlenec 2 foreach( $versions as $version ) { $categories[$name]['Versions'][$version] = $hasLanguageNode ? array_unique(array_merge(array('export all'), $languages[$version])) : array(); $JSCategories .= "'$version':['" . implode("','", $categories[$name]['Versions'][$version]) . "']" . ( $version != end($versions) ? ',' : '' ); } $JSCategories .= "}" . ( $name != end(array_keys($categories)) ? ',' : '' ); } $JSCategories .= "};"; if ( empty( $_REQUEST['Category']) ) $_REQUEST['Category'] = array_shift(array_keys($categories)); if ( empty( $_REQUEST['Version']) && !empty($categories[$_REQUEST['Category']]['Versions']) ) $_REQUEST['Version'] = array_shift(array_keys($categories[$_REQUEST['Category']]['Versions'])); $nodes = "var node = this.parentNode.parentNode.getElementsByTagName('select');"; $removeNodes = "while(node[i].hasChildNodes()) { node[i].removeChild(node[i].firstChild) }"; $createVersionNode = "for ( var vers in categories[cat] ) { var opt = document.createElement('option'); opt.appendChild(document.createTextNode(vers)); node[i].appendChild(opt) } "; $createLanguageNode = "for ( var lang in categories[cat][version] ) { var opt = document.createElement('option'); opt.appendChild(document.createTextNode(categories[cat][version][lang])); node[i].appendChild(opt) } "; $nodeValues = "var cat = null; var version = null; for( var i=0; idatabase->databaseConnection->query("SET NAMES 'utf8' COLLATE 'utf8_general_ci'"); $this->database->databaseConnection->query("SET CHARACTER SET 'utf8'"); } /** * Allow the plugin to prevent DokuWiki creating a second instance of itself * * @return bool true if the plugin can not be instantiated more than once */ function isSingleton() { return true; } /** * PrŸfen, ob die aktuelle Version schonmal hinsichtlich der Datenbankanbindung geprŸft wurde */ function currentVersionChecked() { if ( !file_exists( dirname(__FILE__).'/.translationcheck' ) ) { return false; } $checkDate = implode('', file( dirname(__FILE__).'/.translationcheck' ) ); $hash = confToHash(dirname(__FILE__).'/info.txt'); return $hash['date'] == trim($checkDate); } /** * Set the current Version to be checked */ function setCurrentVersionChecked() { $file = dirname(__FILE__).'/.translationcheck'; $fh = fopen($file, 'w'); if ( !$fh ) { msg('Could not write version check file.'); return false; } $hash = confToHash(dirname(__FILE__).'/info.txt'); fwrite( $fh, $hash['date']); fclose($fh); return true; } } //Setup VIM: ex: et ts=4 enc=utf-8 :