From 94038ce60e6435eea5a11a3e99a16ca79420d526 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 15 Jul 2011 16:58:14 +0200 Subject: [PATCH] Added processor for language servers --- .../LanguageServerProcessor.class.php | 348 ++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 wcfsetup/install/files/lib/system/language/LanguageServerProcessor.class.php diff --git a/wcfsetup/install/files/lib/system/language/LanguageServerProcessor.class.php b/wcfsetup/install/files/lib/system/language/LanguageServerProcessor.class.php new file mode 100644 index 0000000000..89f1b95dff --- /dev/null +++ b/wcfsetup/install/files/lib/system/language/LanguageServerProcessor.class.php @@ -0,0 +1,348 @@ + + * @package com.woltlab.wcf + * @subpackage system.language + * @category Community Framework + */ +class LanguageServerProcessor extends SingletonFactory { + /** + * language object + * @var wcf\data\language\Language + */ + protected $language = null; + + /** + * Imports language variables for a language from given language servers. + * + * @param wcf\data\language\Language $language + * @param array $languageServers + */ + public function import(Language $language, array $languageServers) { + if (!count($languageServers)) return; + $this->language = $language; + + // get package list + $packageList = $this->getPackageList(); + + foreach ($languageServers as $languageServer) { + $this->importLanguageFile($languageServer->serverURL, $packageList); + } + } + + /** + * Returns list of installed packages and their associated version. + * + * @return array + */ + protected function getPackageList() { + $sql = "SELECT package, packageVersion + FROM wcf".WCF_N."_package"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute(); + + $packages = array(); + while ($row = $statement->fetchArray()) { + $packages[$row['package']] = $row['packageVersion']; + } + + return $packages; + } + + /** + * Downloads and imports a language file from a language server. + * + * @param string $location + * @param array $packageList + */ + protected function importLanguageFile($location, array $packageList) { + // get proxy + $options = array(); + if (PROXY_SERVER_HTTP) { + $options['http']['proxy'] = PROXY_SERVER_HTTP; + $options['http']['request_fulluri'] = true; + } + + // parse url + $parsedURL = parse_url($location); + $port = ($parsedURL['scheme'] == 'https' ? 443 : 80); + $host = $parsedURL['host']; + $path = (isset($parsedURL['path']) ? $parsedURL['path'] : '/'); + + $remoteFile = new RemoteFile(($parsedURL['scheme'] == 'https' ? 'ssl://' : '').$host, $port, 30, $options); // the file to read. + if (!isset($remoteFile)) { + throw new SystemException("cannot connect to http host '".$host."'", 14000); + } + + // build and send the http request + $request = "POST ".$path." HTTP/1.0\r\n"; + $request .= "User-Agent: HTTP.PHP (LanguageServerProcessor.class.php; WoltLab Community Framework/".WCF_VERSION."; ".WCF::getLanguage()->languageCode.")\r\n"; + $request .= "Accept: */*\r\n"; + $request .= "Accept-Language: ".WCF::getLanguage()->languageCode."\r\n"; + $request .= "Host: ".$host."\r\n"; + + // build post string + $postString = 'languageCode='.$this->language->languageCode; + foreach ($packageList as $package => $packageVersion) { + $postString .= '&packages['.urlencode($package).']='.urlencode($packageVersion); + } + + // send content type and length + $request .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $request .= "Content-Length: ".strlen($postString)."\r\n"; + // if it is a POST request, there MUST be a blank line before the POST data, but there MUST NOT be + // another blank line before, and of course there must be another blank line at the end of the request! + $request .= "\r\n"; + if (!empty($postString)) $request .= $postString."\r\n"; + // send close + $request .= "Connection: Close\r\n\r\n"; + + // send request + $remoteFile->puts($request); + + // define response vars + $header = $content = ''; + + // fetch the response. + while (!$remoteFile->eof()) { + $line = $remoteFile->gets(); + if (rtrim($line) != '') { + $header .= $line; + } else { + break; + } + } + while (!$remoteFile->eof()) { + $content .= $remoteFile->gets(); + } + + // clean up and return the server's response. + $remoteFile->close(); + + // get http status code / line + $httpStatusCode = 0; + $httpStatusLine = ''; + if (preg_match('%http/\d\.\d (\d{3})[^\n]*%i', $header, $match)) { + $httpStatusLine = trim($match[0]); + $httpStatusCode = $match[1]; + } + + // catch http 301 Moved Permanently + // catch http 302 Found + // catch http 303 See Other + if ($httpStatusCode == 301 || $httpStatusCode == 302 || $httpStatusCode == 303) { + // find location + if (preg_match('/location:([^\n]*)/i', $header, $match)) { + $location = trim($match[1]); + if ($location != $url) { + $this->importLanguageFile($location, $packageList); + return; + } + } + } + + $this->parseResponse($content); + } + + /** + * Parses XML response from language server. + * + * @param string $xmlResponse + */ + protected function parseResponse($xmlResponse) { + // parse xml + $xml = new XML(); + $xml->loadXML('languageServerResponse.xml', $xmlResponse); + $xpath = $xml->xpath(); + + // parse variables + $variables = array(); + $packages = $xpath->query('/ns:language/ns:package'); + foreach ($packages as $package) { + $packageName = $package->getAttribute('name'); + $variables[$packageName] = array(); + + $categories = $xpath->query('child::ns:category', $package); + foreach ($categories as $category) { + $categoryName = $category->getAttribute('name'); + $variables[$packageName][$categoryName] = array(); + + $items = $xpath->query('child::ns:item', $category); + foreach ($items as $item) { + $variables[$packageName][$categoryName][$item->getAttribute('name')] = $item->nodeValue; + } + } + } + + // try to resolve packages + $conditions = new PreparedStatementConditionBuilder(); + $conditions->add("package IN (?)", array(array_keys($variables))); + + $sql = "SELECT packageID, package + FROM wcf".WCF_N."_package + ".$conditions; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($conditions->getParameters()); + $packages = array(); + while ($row = $statement->fetchArray()) { + $packages[$row['package']] = $row['packageID']; + } + + // ignore variables if no package is known + if (!count($packages)) return; + + $this->importVariables($variables, $packages); + } + + /** + * Imports language variables and categories. + * + * @param array $variables + * @param array $packages + */ + protected function importVariables(array $variables, array $packages) { + $categories = $this->importCategories($variables); + + $createItems = $updateItems = array(); + foreach ($packages as $package => $packageID) { + foreach ($variables[$package] as $category => $items) { + // get existing items + $conditions = new PreparedStatementConditionBuilder(); + $conditions->add("languageID = ?", array($this->language->languageID)); + $conditions->add("packageID = ?", array($packageID)); + $conditions->add("languageCategoryID = ?", array($categories[$category])); + $conditions->add("languageItem IN (?)", array(array_keys($items))); + + $sql = "SELECT languageItemID, languageItem + FROM wcf".WCF_N."_language_item + ".$conditions; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($conditions->getParameters()); + + $existingItemIDs = array(); + while ($row = $statement->fetchArray()) { + $existingItemIDs[$row['languageItem']] = $row['languageItemID']; + } + + foreach ($items as $itemName => $itemValue) { + if (isset($existingItemIDs[$itemName])) { + $updateItems[$existingItemIDs[$itemName]] = $itemValue; + } + else { + $createItems[] = array( + 'languageID' => $this->language->languageID, + 'languageItem' => $itemName, + 'languageItemValue'=> $itemValue, + 'languageItemOriginIsSystem' => 1, + 'languageCategoryID' => $categories[$category], + 'packageID' => $packageID + ); + } + } + } + } + + // create items + if (count($createItems)) { + $sql = "INSERT INTO wcf".WCF_N."_language_item + (languageID, languageItem, languageItemValue, languageItemOriginIsSystem, languageCategoryID, packageID) + VALUES (?, ?, ?, ?, ?, ?)"; + $statement = WCF::getDB()->prepareStatement($sql); + + foreach ($createItems as $item) { + $statement->execute(array( + $item['languageID'], + $item['languageItem'], + $item['languageItemValue'], + $item['languageItemOriginIsSystem'], + $item['languageCategoryID'], + $item['packageID'] + )); + } + } + + // update items + if (count($updateItems)) { + $sql = "UPDATE wcf".WCF_N."_language_item + SET languageItemValue = ? + WHERE languageItemID = ?"; + $statement = WCF::getDB()->prepareStatement($sql); + + foreach ($updateItems as $languageItemID => $languageItemValue) { + $statement->execute(array($languageItemID, $languageItemValue)); + } + } + } + + /** + * Imports new langage categories. + * + * @param array $variables + * @return array + */ + protected function importCategories(array $variables) { + // get categories + $categoryNames = array(); + foreach ($variables as $package => $dummy) { + $categoryNames = array_merge($categoryNames, array_keys($variables[$package])); + } + + // fetch existing categories + $conditions = new PreparedStatementConditionBuilder(); + $conditions->add("languageCategory IN (?)", array($categoryNames)); + + $sql = "SELECT languageCategoryID, languageCategory + FROM wcf".WCF_N."_language_category + ".$conditions; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($conditions->getParameters()); + + $existingCategories = array(); + while ($row = $statement->fetchArray()) { + $existingCategories[$row['languageCategory']] = $row['languageCategoryID']; + } + + // create non-existing categories + $createCategories = array_diff($categoryNames, array_keys($existingCategories)); + if (count($createCategories)) { + // use raw queries for better performance + $sql = "INSERT INTO wcf".WCF_N."_language_category + (languageCategory) + VALUES (?)"; + $statement = WCF::getDB()->prepareStatement($sql); + foreach ($createCategories as $category) { + $statement->execute(array($category)); + } + + // get ids for created categories + $conditions = new PreparedStatementConditionBuilder(); + $conditions->add("languageCategory IN (?)", array($createCategories)); + + $sql = "SELECT languageCategoryID, languageCategory + FROM wcf".WCF_N."_language_category + ".$conditions; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($conditions->getParameters()); + + while ($row = $statement->fetchArray()) { + $existingCategories[$row['languageCategory']] = $row['languageCategoryID']; + } + } + + return $existingCategories; + } +} -- 2.20.1