b214f753459e90456551e810daf1f138ba793f4f
[Packages/TYPO3.CMS.git] / typo3 / sysext / lang / Classes / Utility / Connection / Ter.php
1 <?php
2 namespace TYPO3\CMS\Lang\Utility\Connection;
3 /***************************************************************
4 * Copyright notice
5 *
6 * (c) 2012-2013 Sebastian Fischer <typo3@evoweb.de>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 *
18 * This script is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * This copyright notice MUST APPEAR in all copies of the script!
24 ***************************************************************/
25
26 /**
27 * Extends of extensionmanager ter connection to enrich with translation related methods
28 *
29 * @author Sebastian Fischer <typo3@evoweb.de>
30 */
31 class Ter extends \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility {
32
33 /**
34 * Fetches extensions translation status
35 *
36 * @param string $extensionKey Extension Key
37 * @param string $mirrorUrl URL of mirror to use
38 * @return mixed
39 */
40 public function fetchTranslationStatus($extensionKey, $mirrorUrl) {
41 $result = FALSE;
42 $extPath = \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($extensionKey);
43 $mirrorUrl .= $extPath{0} . '/' . $extPath{1} . '/' . $extPath . '-l10n/' . $extPath . '-l10n.xml';
44 $remote = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
45
46 if ($remote !== FALSE) {
47 $parsed = $this->parseL10nXML($remote);
48 $result = $parsed['languagePackIndex'];
49 }
50
51 return $result;
52 }
53
54 /**
55 * Parses content of *-l10n.xml into a suitable array
56 *
57 * @param string $string: XML data to parse
58 * @throws \TYPO3\CMS\Lang\Exception\XmlParser
59 * @return array Array representation of XML data
60 */
61 protected function parseL10nXML($string) {
62 // Create parser:
63 $parser = xml_parser_create();
64 $values = array();
65 $index = array();
66
67 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
68 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
69
70 // Parse content
71 xml_parse_into_struct($parser, $string, $values, $index);
72
73 // If error, return error message
74 if (xml_get_error_code($parser)) {
75 $line = xml_get_current_line_number($parser);
76 $error = xml_error_string(xml_get_error_code($parser));
77 xml_parser_free($parser);
78 throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error in XML parser while decoding l10n XML file. Line ' . $line . ': ' . $error, 1345736517);
79 } else {
80 // Init vars
81 $stack = array(array());
82 $stacktop = 0;
83 $current = array();
84 $tagName = '';
85 $documentTag = '';
86
87 // Traverse the parsed XML structure:
88 foreach ($values as $val) {
89 // First, process the tag-name (which is used in both cases, whether "complete" or "close")
90 $tagName = ($val['tag'] == 'languagepack' && $val['type'] == 'open') ? $val['attributes']['language'] : $val['tag'];
91 if (!$documentTag) {
92 $documentTag = $tagName;
93 }
94
95 // Setting tag-values, manage stack:
96 switch ($val['type']) {
97 // If open tag it means there is an array stored in sub-elements.
98 // Therefore increase the stackpointer and reset the accumulation array
99 case 'open':
100 // Setting blank place holder
101 $current[$tagName] = array();
102 $stack[$stacktop++] = $current;
103 $current = array();
104 break;
105 // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
106 case 'close':
107 $oldCurrent = $current;
108 $current = $stack[--$stacktop];
109 // Going to the end of array to get placeholder key, key($current), and fill in array next
110 end($current);
111 $current[key($current)] = $oldCurrent;
112 unset($oldCurrent);
113 break;
114 // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
115 case 'complete':
116 // Had to cast it as a string - otherwise it would be evaluate FALSE if tested with isset()!!
117 $current[$tagName] = (string) $val['value'];
118 break;
119 }
120 }
121 $result = $current[$tagName];
122 }
123
124 return $result;
125 }
126
127 /**
128 * Install translations for all selected languages for an extension
129 *
130 * @param string $extensionKey The extension key to install the translations for
131 * @param string $language Language code of translation to fetch
132 * @param string $mirrorUrl Mirror URL to fetch data from
133 * @return boolean TRUE on success, error string on fauilure
134 */
135 public function updateTranslation($extensionKey, $language, $mirrorUrl) {
136 $result = FALSE;
137 try {
138 $l10n = $this->fetchTranslation($extensionKey, $language, $mirrorUrl);
139 if (is_array($l10n)) {
140 $absolutePathToZipFile = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName('typo3temp/' . $extensionKey . '-l10n-' . $language . '.zip');
141 $relativeLanguagePath = 'l10n' . '/' . $language . '/';
142 $absoluteLanguagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath);
143 $absoluteExtensionLanguagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath. $extensionKey . '/');
144 if (empty($absolutePathToZipFile) || empty($absoluteLanguagePath) || empty($absoluteExtensionLanguagePath)) {
145 throw new \TYPO3\CMS\Lang\Exception\Lang('Given path is invalid.', 1352565336);
146 }
147 if (!is_dir($absoluteLanguagePath)) {
148 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep(PATH_typo3conf, $relativeLanguagePath);
149 }
150 \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($absolutePathToZipFile, $l10n[0]);
151 if (is_dir($absoluteExtensionLanguagePath)) {
152 \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir($absoluteExtensionLanguagePath, TRUE);
153 }
154
155 if ($this->unzipTranslationFile($absolutePathToZipFile, $absoluteLanguagePath)) {
156 $result = TRUE;
157 }
158 }
159 } catch (\TYPO3\CMS\Core\Exception $exception) {
160 // @todo logging
161 }
162 return $result;
163 }
164
165 /**
166 * Fetches an extensions l10n file from the given mirror
167 *
168 * @param string $extensionKey Extension Key
169 * @param string $language The language code of the translation to fetch
170 * @param string $mirrorUrl URL of mirror to use
171 * @throws \TYPO3\CMS\Lang\Exception\XmlParser
172 * @return array Array containing l10n data
173 */
174 protected function fetchTranslation($extensionKey, $language, $mirrorUrl) {
175 $extensionPath = \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($extensionKey);
176 $mirrorUrl .= $extensionPath{0} . '/' . $extensionPath{1} . '/' . $extensionPath .
177 '-l10n/' . $extensionPath . '-l10n-' . $language . '.zip';
178 $l10nResponse = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
179
180 if ($l10nResponse === FALSE) {
181 throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error: Translation could not be fetched.', 1345736785);
182 } else {
183 return array($l10nResponse);
184 }
185 }
186
187 /**
188 * Unzip an language.zip.
189 *
190 * @param string $file path to zip file
191 * @param string $path path to extract to
192 * @throws \TYPO3\CMS\Lang\Exception\Lang
193 * @return boolean
194 */
195 protected function unzipTranslationFile($file, $path) {
196 $zip = zip_open($file);
197 if (is_resource($zip)) {
198 $result = TRUE;
199
200 if (!is_dir($path)) {
201 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path);
202 }
203
204 while (($zipEntry = zip_read($zip)) !== FALSE) {
205 $zipEntryName = zip_entry_name($zipEntry);
206 if (strpos($zipEntryName, '/') !== FALSE) {
207 $zipEntryPathSegments = explode('/', $zipEntryName);
208 $fileName = array_pop($zipEntryPathSegments);
209 // It is a folder, because the last segment is empty, let's create it
210 if (empty($fileName)) {
211 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path, implode('/', $zipEntryPathSegments));
212 } else {
213 $absoluteTargetPath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($path . implode('/', $zipEntryPathSegments) . '/' . $fileName);
214 if (strlen(trim($absoluteTargetPath)) > 0) {
215 $return = \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile(
216 $absoluteTargetPath, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry))
217 );
218 if ($return === FALSE) {
219 throw new \TYPO3\CMS\Lang\Exception\Lang('Could not write file ' . $zipEntryName, 1345304560);
220 }
221 } else {
222 throw new \TYPO3\CMS\Lang\Exception\Lang('Could not write file ' . $zipEntryName, 1352566904);
223 }
224 }
225 } else {
226 throw new \TYPO3\CMS\Lang\Exception\Lang('Extension directory missing in zip file!', 1352566904);
227 }
228 }
229 } else {
230 throw new \TYPO3\CMS\Lang\Exception\Lang('Unable to open zip file ' . $file, 1345304561);
231 }
232
233 return $result;
234 }
235 }
236
237 ?>