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