TYPO3 CMS  TYPO3_7-6
TerService.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Lang\Service;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
19 
25 {
33  public function fetchTranslationStatus($extensionKey, $mirrorUrl)
34  {
35  $result = false;
36  $extPath = GeneralUtility::strtolower($extensionKey);
37  $mirrorUrl .= $extPath[0] . '/' . $extPath[1] . '/' . $extPath . '-l10n/' . $extPath . '-l10n.xml';
38  $remote = GeneralUtility::getURL($mirrorUrl, 0, [TYPO3_user_agent]);
39  if ($remote !== false) {
40  $parsed = $this->parseL10nXML($remote);
41  $result = $parsed['languagePackIndex'];
42  }
43  return $result;
44  }
45 
53  protected function parseL10nXML($string)
54  {
55  // Create parser:
56  $parser = xml_parser_create();
57  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
58  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
59  $values = [];
60  $index = [];
61  xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
62  xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
63  // Parse content
64  xml_parse_into_struct($parser, $string, $values, $index);
65  libxml_disable_entity_loader($previousValueOfEntityLoader);
66  // If error, return error message
67  if (xml_get_error_code($parser)) {
68  $line = xml_get_current_line_number($parser);
69  $error = xml_error_string(xml_get_error_code($parser));
70  xml_parser_free($parser);
71  throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error in XML parser while decoding l10n XML file. Line ' . $line . ': ' . $error, 1345736517);
72  } else {
73  // Init vars
74  $stack = [[]];
75  $stacktop = 0;
76  $current = [];
77  $tagName = '';
78  $documentTag = '';
79  // Traverse the parsed XML structure:
80  foreach ($values as $val) {
81  // First, process the tag-name (which is used in both cases, whether "complete" or "close")
82  $tagName = ($val['tag'] == 'languagepack' && $val['type'] == 'open') ? $val['attributes']['language'] : $val['tag'];
83  if (!$documentTag) {
84  $documentTag = $tagName;
85  }
86  // Setting tag-values, manage stack:
87  switch ($val['type']) {
88  // If open tag it means there is an array stored in sub-elements.
89  // Therefore increase the stackpointer and reset the accumulation array
90  case 'open':
91  // Setting blank place holder
92  $current[$tagName] = [];
93  $stack[$stacktop++] = $current;
94  $current = [];
95  break;
96  // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
97  case 'close':
98  $oldCurrent = $current;
99  $current = $stack[--$stacktop];
100  // Going to the end of array to get placeholder key, key($current), and fill in array next
101  end($current);
102  $current[key($current)] = $oldCurrent;
103  unset($oldCurrent);
104  break;
105  // If "complete", then it's a value. Omits the tag if the value is empty.
106  case 'complete':
107  $trimmedValue = trim((string)$val['value']);
108  if ($trimmedValue !== '') {
109  $current[$tagName] = $trimmedValue;
110  }
111  break;
112  }
113  }
114  $result = $current[$tagName];
115  }
116  return $result;
117  }
118 
127  public function updateTranslation($extensionKey, $language, $mirrorUrl)
128  {
129  $result = false;
130  try {
131  $l10n = $this->fetchTranslation($extensionKey, $language, $mirrorUrl);
132  if (is_array($l10n)) {
133  $absolutePathToZipFile = GeneralUtility::getFileAbsFileName('typo3temp/Language/' . $extensionKey . '-l10n-' . $language . '.zip');
134  $relativeLanguagePath = 'l10n' . '/' . $language . '/';
135  $absoluteLanguagePath = GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath);
136  $absoluteExtensionLanguagePath = GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath . $extensionKey . '/');
137  if (empty($absolutePathToZipFile) || empty($absoluteLanguagePath) || empty($absoluteExtensionLanguagePath)) {
138  throw new \TYPO3\CMS\Lang\Exception\Language('Given path is invalid.', 1352565336);
139  }
140  if (!is_dir($absoluteLanguagePath)) {
141  GeneralUtility::mkdir_deep(PATH_typo3conf, $relativeLanguagePath);
142  }
143  GeneralUtility::writeFileToTypo3tempDir($absolutePathToZipFile, $l10n[0]);
144  if (is_dir($absoluteExtensionLanguagePath)) {
145  GeneralUtility::rmdir($absoluteExtensionLanguagePath, true);
146  }
147  if ($this->unzipTranslationFile($absolutePathToZipFile, $absoluteLanguagePath)) {
148  $result = true;
149  }
150  }
151  } catch (\TYPO3\CMS\Core\Exception $exception) {
152  // @todo logging
153  }
154  return $result;
155  }
156 
166  protected function fetchTranslation($extensionKey, $language, $mirrorUrl)
167  {
168  $extensionPath = GeneralUtility::strtolower($extensionKey);
169  // Typical non sysext path, Hungarian:
170  // http://my.mirror/ter/a/n/anextension-l10n/anextension-l10n-hu.zip
171  $packageUrl = $extensionPath[0] . '/' . $extensionPath[1] . '/' . $extensionPath .
172  '-l10n/' . $extensionPath . '-l10n-' . $language . '.zip';
173 
174  try {
175  $path = ExtensionManagementUtility::extPath($extensionPath);
176  if (strpos($path, '/sysext/') !== false) {
177  // This is a system extension and the package URL should be adapted
178  list($majorVersion, ) = explode('.', TYPO3_branch);
179  // Typical non sysext path, mind the additional version part, French
180  // http://my.mirror/ter/b/a/backend-l10n/backend-l10n-fr.v7.zip
181  $packageUrl = $extensionPath[0] . '/' . $extensionPath[1] . '/' . $extensionPath .
182  '-l10n/' . $extensionPath . '-l10n-' . $language . '.v' . $majorVersion . '.zip';
183  }
184  } catch (\BadFunctionCallException $e) {
185  // Nothing to do
186  }
187 
188  $l10nResponse = GeneralUtility::getURL($mirrorUrl . $packageUrl, 0, [TYPO3_user_agent]);
189  if ($l10nResponse === false) {
190  throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error: Translation could not be fetched.', 1345736785);
191  } else {
192  return [$l10nResponse];
193  }
194  }
195 
204  protected function unzipTranslationFile($file, $path)
205  {
206  $zip = zip_open($file);
207  if (is_resource($zip)) {
208  $result = true;
209  if (!is_dir($path)) {
211  }
212  while (($zipEntry = zip_read($zip)) !== false) {
213  $zipEntryName = zip_entry_name($zipEntry);
214  if (strpos($zipEntryName, '/') !== false) {
215  $zipEntryPathSegments = explode('/', $zipEntryName);
216  $fileName = array_pop($zipEntryPathSegments);
217  // It is a folder, because the last segment is empty, let's create it
218  if (empty($fileName)) {
219  GeneralUtility::mkdir_deep($path, implode('/', $zipEntryPathSegments));
220  } else {
221  $absoluteTargetPath = GeneralUtility::getFileAbsFileName($path . implode('/', $zipEntryPathSegments) . '/' . $fileName);
222  if (trim($absoluteTargetPath) !== '') {
223  $return = GeneralUtility::writeFile(
224  $absoluteTargetPath, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry))
225  );
226  if ($return === false) {
227  throw new \TYPO3\CMS\Lang\Exception\Language('Could not write file ' . $zipEntryName, 1345304560);
228  }
229  } else {
230  throw new \TYPO3\CMS\Lang\Exception\Language('Could not write file ' . $zipEntryName, 1352566904);
231  }
232  }
233  } else {
234  throw new \TYPO3\CMS\Lang\Exception\Language('Extension directory missing in zip file!', 1352566905);
235  }
236  }
237  } else {
238  throw new \TYPO3\CMS\Lang\Exception\Language('Unable to open zip file ' . $file, 1345304561);
239  }
240  return $result;
241  }
242 }
static mkdir_deep($directory, $deepDirectory='')
static writeFileToTypo3tempDir($filepath, $content)
$remote
Definition: server.php:70
fetchTranslation($extensionKey, $language, $mirrorUrl)
Definition: TerService.php:166
static rmdir($path, $removeNonEmpty=false)
updateTranslation($extensionKey, $language, $mirrorUrl)
Definition: TerService.php:127
static getFileAbsFileName($filename, $onlyRelative=true, $relToTYPO3_mainDir=false)
fetchTranslationStatus($extensionKey, $mirrorUrl)
Definition: TerService.php:33
static writeFile($file, $content, $changePermissions=false)