4d5f2e345aa07b4511b9444ef93f3f13c3d9ebac
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Updates / InstallSysExtsUpdate.php
1 <?php
2 namespace TYPO3\CMS\Install\Updates;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2008-2013 Benjamin Mack <benni@typo3.org>
8 * (c) 2008-2013 Steffen Kamper <info@sk-typo3.de>
9 * (c) 2012-2013 Kai Vogel <kai.vogel@speedprogs.de>
10 * All rights reserved
11 *
12 * This script is part of the TYPO3 project. The TYPO3 project is
13 * free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * The GNU General Public License can be found at
19 * http://www.gnu.org/copyleft/gpl.html.
20 * A copy is found in the textfile GPL.txt and important notices to the license
21 * from the author is found in LICENSE.txt distributed with these scripts.
22 *
23 *
24 * This script is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * This copyright notice MUST APPEAR in all copies of the script!
30 ***************************************************************/
31
32 /**
33 * Contains the update class for adding new system extensions.
34 *
35 * @author Benjamin Mack <benni@typo3.org>
36 * @author Steffen Kamper <info@sk-typo3.de>
37 * @author Kai Vogel <kai.vogel@speedprogs.de>
38 */
39 class InstallSysExtsUpdate extends AbstractUpdate {
40
41 /**
42 * @var string
43 */
44 protected $title = 'Install System Extensions';
45
46 /**
47 * @var array
48 */
49 protected $systemExtensions = array(
50 'info',
51 'perm',
52 'func',
53 'filelist',
54 'about',
55 'cshmanual',
56 'feedit',
57 'opendocs',
58 'recycler',
59 't3editor',
60 'reports',
61 'scheduler',
62 'simulatestatic',
63 'documentation',
64 'rsaauth',
65 );
66
67 /**
68 * @var array
69 */
70 protected $extensionDetails = array(
71 'simulatestatic' => array(
72 'title' => 'Simulate Static URLs',
73 'description' => 'Adds the possibility to have Speaking URLs in the TYPO3 Frontend pages.',
74 'versionString' => '2.0.0',
75 ),
76 );
77
78 /**
79 * @var string
80 */
81 protected $extEmConfPath = 'typo3/sysext/@extensionKey/ext_emconf.php';
82
83 /**
84 * @var string
85 */
86 protected $repositoryUrl = 'http://typo3.org/fileadmin/ter/@filename';
87
88 /**
89 * @var string
90 */
91 protected $informationUrl = 'http://typo3.org/index.php?type=95832&tx_terfe2_pi1%5Baction%5D=show&tx_terfe2_pi1%5Bformat%5D=json&tx_terfe2_pi1%5BextensionKey%5D=@extensionKey';
92
93 /**
94 * @var boolean
95 */
96 protected $updateSuccessful = TRUE;
97
98 /**
99 * Checks if an update is needed
100 *
101 * @param string &$description The description for the update
102 * @return boolean whether an update is needed (TRUE) or not (FALSE)
103 */
104 public function checkForUpdate(&$description) {
105 $description = '
106 <br />
107 Uninstalled system extensions have been found.
108 It is now possible to install them automatically by this upgrade wizard.
109 ';
110
111 if ($this->isWizardDone()) {
112 return FALSE;
113 }
114
115 foreach ($this->systemExtensions as $extensionKey) {
116 if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extensionKey)) {
117 return TRUE;
118 }
119 }
120
121 return FALSE;
122 }
123
124 /**
125 * Second step: Get user input for installing system extensions
126 *
127 * @param string $inputPrefix input prefix, all names of form fields have to start with this. Append custom name in [ ... ]
128 * @return string HTML output
129 */
130 public function getUserInput($inputPrefix) {
131 $list = '
132 <p>
133 Install the following system extensions:
134 </p>
135 <fieldset>
136 <ol class="t3-install-form-label-after">%s</ol>
137 </fieldset>';
138 $item = '
139 <li class="labelAfter">
140 <input type="checkbox" id="%1$s" name="%2$s[sysext][%1$s]" value="1" checked="checked" />
141 <label for="%1$s"><strong>%3$s [%1$s]</strong><br />%4$s</label>
142 </li>';
143 $items = array();
144
145 foreach ($this->systemExtensions as $extensionKey) {
146 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extensionKey)) {
147 continue;
148 }
149 $extension = $this->getExtensionDetails($extensionKey);
150 $items[] = sprintf(
151 $item,
152 $extensionKey,
153 $inputPrefix,
154 htmlspecialchars($extension['title']),
155 htmlspecialchars($extension['description'])
156 );
157 }
158
159 return sprintf($list, implode('', $items));
160 }
161
162 /**
163 * Adds the outsourced extensions to the extList in TYPO3_CONF_VARS
164 *
165 * @param array &$dbQueries Queries done in this update
166 * @param mixed &$customMessages Custom messages
167 * @return boolean whether it worked (TRUE) or not (FALSE)
168 */
169 public function performUpdate(array &$dbQueries, &$customMessages) {
170 // Get extension keys that were submitted by the user to be installed and that are valid for this update wizard
171 if (is_array($this->userInput['sysext'])) {
172 $extArray = array_intersect(
173 $this->systemExtensions,
174 array_keys($this->userInput['sysext'])
175 );
176 $this->installExtensions($extArray, $customMessages);
177 }
178
179 // Show this wizard again only if new extension keys have been found
180 $this->markWizardAsDone();
181
182 return $this->updateSuccessful;
183 }
184
185 /**
186 * This method can be called to install extensions following all proper processes
187 * (e.g. installing in extList, respecting priority, etc.)
188 *
189 * @param array $extensionKeys List of keys of extensions to install
190 * @param mixed $customMessages
191 * @return void
192 */
193 protected function installExtensions($extensionKeys, &$customMessages) {
194 /** @var $extensionListUtility \TYPO3\CMS\Extensionmanager\Utility\ListUtility */
195 $extensionListUtility = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
196 'TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility'
197 );
198
199 /** @var $extensionFileHandlingUtility \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility */
200 $extensionFileHandlingUtility = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
201 'TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility'
202 );
203 /** @var $objectManager \TYPO3\CMS\Extbase\Object\ObjectManager */
204 $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
205
206 /** @var $extensionInstallUtility \TYPO3\CMS\Extensionmanager\Utility\InstallUtility */
207 $extensionInstallUtility = $objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\InstallUtility');
208
209 /** @var $extensionTerUtility \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility */
210 $extensionTerUtility = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
211 'TYPO3\\CMS\\Extensionmanager\\Utility\\Connection\\TerUtility'
212 );
213 $availableExtensions = $extensionListUtility->getAvailableExtensions();
214 $availableAndInstalledExtensions = $extensionListUtility->getAvailableAndInstalledExtensions($availableExtensions);
215 foreach ($extensionKeys as $extensionKey) {
216 if (!is_array($availableAndInstalledExtensions[$extensionKey])) {
217 $extensionDetails = $this->getExtensionDetails($extensionKey);
218 if (empty($extensionDetails)) {
219 $this->updateSuccessful = FALSE;
220 $customMessages .= 'No version information for extension ' . $extensionKey . '. Cannot install it.';
221 continue;
222 }
223 $t3xContent = $this->fetchExtension($extensionKey, $extensionDetails['versionString']);
224 if (empty($t3xContent)) {
225 $this->updateSuccessful = FALSE;
226 $customMessages .= 'The extension ' . $extensionKey . ' could not be downloaded.';
227 continue;
228 }
229 $t3xExtracted = $extensionTerUtility->decodeExchangeData($t3xContent);
230 if (empty($t3xExtracted) || !is_array($t3xExtracted) || empty($t3xExtracted['extKey'])) {
231 $this->updateSuccessful = FALSE;
232 $customMessages .= 'The extension ' . $extensionKey . ' could not be extracted.';
233 continue;
234 }
235 $extensionFileHandlingUtility->unpackExtensionFromExtensionDataArray($t3xExtracted);
236 }
237 $extensionInstallUtility->install($extensionKey);
238 }
239 }
240
241 /**
242 * Returns the details of a local or external extension
243 *
244 * @param string $extensionKey Key of the extension to check
245 * @return array Extension details
246 */
247 protected function getExtensionDetails($extensionKey) {
248 // Local extension
249 $extEmConf = PATH_site . str_replace('@extensionKey', $extensionKey, $this->extEmConfPath);
250 if (file_exists($extEmConf)) {
251 $EM_CONF = FALSE;
252 require_once($extEmConf);
253 return reset($EM_CONF);
254 }
255
256 if (array_key_exists($extensionKey, $this->extensionDetails)) {
257 return $this->extensionDetails[$extensionKey];
258 }
259
260 return array();
261 }
262
263 /**
264 * Fetch extension from repository
265 *
266 * @param string $extensionKey The extension key to fetch
267 * @param string $version The version to fetch
268 * @return string T3X file content
269 */
270 protected function fetchExtension($extensionKey, $version) {
271 if (empty($extensionKey) || empty($version)) {
272 return '';
273 }
274
275 $filename = $extensionKey[0] . '/' . $extensionKey[1] . '/' . $extensionKey . '_' . $version . '.t3x';
276 $url = str_replace('@filename', $filename, $this->repositoryUrl);
277 return $this->fetchUrl($url);
278 }
279
280 /**
281 * Open an URL and return the response
282 *
283 * This wrapper method is required to try several download methods if
284 * the configuration is not valid or initially written by the installer.
285 *
286 * @param string $url The URL to file
287 * @throws \Exception
288 * @return string File content
289 */
290 protected function fetchUrl($url) {
291 if (empty($url)) {
292 return NULL;
293 }
294
295 $fileContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($url, 0, array(TYPO3_user_agent));
296
297 // No content, force cURL if disabled in configuration but found in system
298 if ($fileContent === FALSE && function_exists('curl_init') && empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'])) {
299 $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] = TRUE;
300 $fileContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($url, 0, array(TYPO3_user_agent));
301 $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] = FALSE;
302 }
303
304 // Still no content, try file_get_contents if allow_url_fopen is enabled
305 if ($fileContent === FALSE && function_exists('file_get_contents') && ini_get('allow_url_fopen')) {
306 $fileContent = file_get_contents($url);
307 }
308
309 // Can not fetch url, throw an exception
310 if ($fileContent === FALSE) {
311 throw new \Exception(
312 'Can not fetch URL "' . $url . '". Possibile reasons are network problems or misconfiguration.',
313 1344685036
314 );
315 }
316
317 return $fileContent;
318 }
319
320 /**
321 * Marks the wizard as being "seen" so that it not shown again until
322 * no new extension keys have been found.
323 *
324 * Writes the info in LocalConfiguration.php
325 *
326 * @return void
327 */
328 protected function markWizardAsDone() {
329 \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager')->setLocalConfigurationValueByPath(
330 'INSTALL/wizardDone/' . get_class($this),
331 json_encode($this->systemExtensions)
332 );
333 }
334
335 /**
336 * Checks if all extensions have been "seen" before
337 *
338 * @return boolean TRUE if wizard has been done before, FALSE otherwise
339 */
340 protected function isWizardDone() {
341 $wizardClassName = get_class($this);
342 if (!empty($GLOBALS['TYPO3_CONF_VARS']['INSTALL']['wizardDone'][$wizardClassName])) {
343 $seenExtensions = json_decode($GLOBALS['TYPO3_CONF_VARS']['INSTALL']['wizardDone'][$wizardClassName], TRUE);
344 return count(array_diff($this->systemExtensions, $seenExtensions)) === 0;
345 }
346 return FALSE;
347 }
348 }
349
350 ?>