[TASK] Use $x[n] instead of substr($x, n, 1)
[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. Omits the tag if the value is empty.
115 case 'complete':
116 $trimmedValue = trim((string)$val['value']);
117 if ($trimmedValue !== '') {
118 $current[$tagName] = $trimmedValue;
119 }
120 break;
121 }
122 }
123 $result = $current[$tagName];
124 }
125
126 return $result;
127 }
128
129 /**
130 * Install translations for all selected languages for an extension
131 *
132 * @param string $extensionKey The extension key to install the translations for
133 * @param string $language Language code of translation to fetch
134 * @param string $mirrorUrl Mirror URL to fetch data from
135 * @return boolean TRUE on success, error string on fauilure
136 */
137 public function updateTranslation($extensionKey, $language, $mirrorUrl) {
138 $result = FALSE;
139 try {
140 $l10n = $this->fetchTranslation($extensionKey, $language, $mirrorUrl);
141 if (is_array($l10n)) {
142 $absolutePathToZipFile = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName('typo3temp/' . $extensionKey . '-l10n-' . $language . '.zip');
143 $relativeLanguagePath = 'l10n' . '/' . $language . '/';
144 $absoluteLanguagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath);
145 $absoluteExtensionLanguagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath. $extensionKey . '/');
146 if (empty($absolutePathToZipFile) || empty($absoluteLanguagePath) || empty($absoluteExtensionLanguagePath)) {
147 throw new \TYPO3\CMS\Lang\Exception\Lang('Given path is invalid.', 1352565336);
148 }
149 if (!is_dir($absoluteLanguagePath)) {
150 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep(PATH_typo3conf, $relativeLanguagePath);
151 }
152 \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($absolutePathToZipFile, $l10n[0]);
153 if (is_dir($absoluteExtensionLanguagePath)) {
154 \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir($absoluteExtensionLanguagePath, TRUE);
155 }
156
157 if ($this->unzipTranslationFile($absolutePathToZipFile, $absoluteLanguagePath)) {
158 $result = TRUE;
159 }
160 }
161 } catch (\TYPO3\CMS\Core\Exception $exception) {
162 // @todo logging
163 }
164 return $result;
165 }
166
167 /**
168 * Fetches an extensions l10n file from the given mirror
169 *
170 * @param string $extensionKey Extension Key
171 * @param string $language The language code of the translation to fetch
172 * @param string $mirrorUrl URL of mirror to use
173 * @throws \TYPO3\CMS\Lang\Exception\XmlParser
174 * @return array Array containing l10n data
175 */
176 protected function fetchTranslation($extensionKey, $language, $mirrorUrl) {
177 $extensionPath = \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($extensionKey);
178 $mirrorUrl .= $extensionPath[0] . '/' . $extensionPath[1] . '/' . $extensionPath .
179 '-l10n/' . $extensionPath . '-l10n-' . $language . '.zip';
180 $l10nResponse = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
181
182 if ($l10nResponse === FALSE) {
183 throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error: Translation could not be fetched.', 1345736785);
184 } else {
185 return array($l10nResponse);
186 }
187 }
188
189 /**
190 * Unzip an language.zip.
191 *
192 * @param string $file path to zip file
193 * @param string $path path to extract to
194 * @throws \TYPO3\CMS\Lang\Exception\Lang
195 * @return boolean
196 */
197 protected function unzipTranslationFile($file, $path) {
198 $zip = zip_open($file);
199 if (is_resource($zip)) {
200 $result = TRUE;
201
202 if (!is_dir($path)) {
203 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path);
204 }
205
206 while (($zipEntry = zip_read($zip)) !== FALSE) {
207 $zipEntryName = zip_entry_name($zipEntry);
208 if (strpos($zipEntryName, '/') !== FALSE) {
209 $zipEntryPathSegments = explode('/', $zipEntryName);
210 $fileName = array_pop($zipEntryPathSegments);
211 // It is a folder, because the last segment is empty, let's create it
212 if (empty($fileName)) {
213 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path, implode('/', $zipEntryPathSegments));
214 } else {
215 $absoluteTargetPath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($path . implode('/', $zipEntryPathSegments) . '/' . $fileName);
216 if (strlen(trim($absoluteTargetPath)) > 0) {
217 $return = \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile(
218 $absoluteTargetPath, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry))
219 );
220 if ($return === FALSE) {
221 throw new \TYPO3\CMS\Lang\Exception\Lang('Could not write file ' . $zipEntryName, 1345304560);
222 }
223 } else {
224 throw new \TYPO3\CMS\Lang\Exception\Lang('Could not write file ' . $zipEntryName, 1352566904);
225 }
226 }
227 } else {
228 throw new \TYPO3\CMS\Lang\Exception\Lang('Extension directory missing in zip file!', 1352566904);
229 }
230 }
231 } else {
232 throw new \TYPO3\CMS\Lang\Exception\Lang('Unable to open zip file ' . $file, 1345304561);
233 }
234
235 return $result;
236 }
237 }