[FEATURE] Allow .ts file extension for static typoscript templates
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_install.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Class to setup values in localconf.php and verify the TYPO3 DB tables/fields
29 *
30 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
31 */
32
33 /**
34 * Class to setup values in localconf.php and verify the TYPO3 DB tables/fields
35 *
36 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
37 * @package TYPO3
38 * @subpackage t3lib
39 */
40 class t3lib_install {
41
42 // External, Static
43 // Set to string which identifies the script using this class.
44 var $updateIdentity = '';
45
46 // Prefix for checkbox fields when updating database.
47 var $dbUpdateCheckboxPrefix = 'TYPO3_INSTALL[database_update]';
48 // If this is set, modifications to localconf.php is done by adding new lines to the array only. If unset, existing values are recognized and changed.
49 var $localconf_addLinesOnly = 0;
50 // If set and addLinesOnly is disabled, lines will be change only if they are after this token (on a single line!) in the file
51 protected $localconf_startEditPointToken = '## INSTALL SCRIPT EDIT POINT TOKEN - all lines after this points may be changed by the install script!';
52 protected $localconf_endEditPointToken = '## INSTALL SCRIPT EDIT END POINT TOKEN - all lines before this points may be changed by the install script!';
53 // If TRUE, this class will allow the user to update the localconf.php file. Is set TRUE in the init.php file.
54 var $allowUpdateLocalConf = 0;
55 // Backpath (used for icons etc.)
56 var $backPath = '../';
57
58 // Internal, dynamic:
59 // Used to indicate that a value is change in the line-array of localconf and that it should be written.
60 var $setLocalconf = 0;
61 // Used to set (error)messages from the executing functions like mail-sending, writing Localconf and such
62 var $messages = array();
63 // Updated with line in localconf.php file that was changed.
64 var $touchedLine = 0;
65
66 /**
67 * @var t3lib_install_Sql Instance of SQL handler
68 */
69 protected $sqlHandler = NULL;
70
71 /**
72 * Constructor function
73 */
74 public function __construct() {
75 $this->sqlHandler = t3lib_div::makeInstance('t3lib_install_Sql');
76 }
77
78 /**************************************
79 *
80 * Writing to localconf.php
81 *
82
83 **************************************/
84
85 /**
86 * This functions takes an array with lines from localconf.php, finds a variable and inserts the new value.
87 *
88 * @param array $line_array The localconf.php file exploded into an array by linebreaks. (see writeToLocalconf_control())
89 * @param string $variable The variable name to find and substitute. This string must match the first part of a trimmed line in the line-array. Matching is done backwards so the last appearing line will be substituted.
90 * @param string $value Is the value to be insert for the variable
91 * @param boolean $quoteValue Whether the given value should be quoted before being written
92 * @return void
93 * @see writeToLocalconf_control()
94 */
95 public function setValueInLocalconfFile(&$line_array, $variable, $value, $quoteValue = TRUE) {
96 if (!$this->checkForBadString($value)) {
97 return 0;
98 }
99
100 // Initialize
101 $found = 0;
102 $this->touchedLine = '';
103 $inArray = in_array($this->localconf_startEditPointToken, $line_array);
104 // Flag is set if the token should be set but is not yet.
105 $tokenSet = ($this->localconf_startEditPointToken && !$inArray);
106 $stopAtToken = ($this->localconf_startEditPointToken && $inArray);
107 $hasEndToken = in_array($this->localconf_endEditPointToken, $line_array);
108 $respectEndToken = $hasEndToken;
109 $comment = ' Modified or inserted by ' . $this->updateIdentity . '.';
110 $replace = array('["', '"]');
111 $search = array('[\'', '\']');
112 $varDoubleQuotes = str_replace($search, $replace, $variable);
113
114 // Search for variable name
115 if (!$this->localconf_addLinesOnly && !$tokenSet) {
116 $line_array = array_reverse($line_array);
117 foreach ($line_array as $k => $v) {
118 $v2 = trim($v);
119 if ($respectEndToken) {
120 if (strcmp($v2, $this->localconf_endEditPointToken)) {
121 $respectEndToken = FALSE;
122 } else {
123 continue;
124 }
125 }
126 if ($stopAtToken && !strcmp($v2, $this->localconf_startEditPointToken)) {
127 break;
128 } // If stopAtToken and token found, break out of the loop..
129 if (!strcmp(substr($v2, 0, strlen($variable . ' ')), $variable . ' ')) {
130 $mainparts = explode($variable, $v, 2);
131 // Should ALWAYS be.
132 if (count($mainparts) == 2) {
133 $subparts = explode('//', $mainparts[1], 2);
134 if ($quoteValue) {
135 $value = '\'' . $this->slashValueForSingleDashes($value) . '\'';
136 }
137 $line_array[$k] = $mainparts[0] . $variable . ' = ' . $value . '; ' . ('//' . $comment . str_replace($comment, '', $subparts[1]));
138 $this->touchedLine = count($line_array) - $k - 1;
139 $found = 1;
140 break;
141 }
142 } elseif (!strcmp(substr($v2, 0, strlen($varDoubleQuotes . ' ')), $varDoubleQuotes . ' ')) {
143 // Due to a bug in the update wizard (fixed in TYPO3 4.1.7) it is possible
144 // that $TYPO3_CONF_VARS['SYS']['compat_version'] was enclosed by "" (double
145 // quotes) instead of the expected '' (single quotes) when is was written to
146 // localconf.php. The following code was added to make sure that values with
147 // double quotes are updated, too.
148 $mainparts = explode($varDoubleQuotes, $v, 2);
149 // Should ALWAYS be.
150 if (count($mainparts) == 2) {
151 $subparts = explode('//', $mainparts[1], 2);
152 if ($quoteValue) {
153 $value = '\'' . $this->slashValueForSingleDashes($value) . '\'';
154 }
155 $line_array[$k] = $mainparts[0] . $variable . ' = ' . $value . '; ' . ('//' . $comment . str_replace($comment, '', $subparts[1]));
156 $this->touchedLine = count($line_array) - $k - 1;
157 $found = 1;
158 break;
159 }
160 }
161 }
162 $line_array = array_reverse($line_array);
163 }
164 if (!$found) {
165 if ($tokenSet) {
166 $line_array[] = $this->localconf_startEditPointToken;
167 $line_array[] = '';
168 }
169 if ($quoteValue) {
170 $value = '\'' . $this->slashValueForSingleDashes($value) . '\'';
171 }
172 $line_array[] = $variable . ' = ' . $value . '; // ' . $comment;
173 if (!$hasEndToken) {
174 $line_array[] = '';
175 $line_array[] = $this->localconf_endEditPointToken;
176 }
177 $this->touchedLine = -1;
178 }
179 if ($variable == '$typo_db_password') {
180 $this->messages[] = 'Updated ' . $variable;
181 } else {
182 $this->messages[] = $variable . ' = ' . htmlspecialchars($value);
183 }
184 $this->setLocalconf = 1;
185 }
186
187 /**
188 * Takes an array with lines from localconf.php, finds a variable and inserts the new array value.
189 *
190 * @param array $lines the localconf.php file exploded into an array by line breaks. {@see writeToLocalconf_control()}
191 * @param string $variable the variable name to find and substitute. This string must match the first part of a trimmed line in the line-array. Matching is done backwards so the last appearing line will be substituted.
192 * @param array $value value to be assigned to the variable
193 * @return void
194 * @see writeToLocalconf_control()
195 */
196 public function setArrayValueInLocalconfFile(array &$lines, $variable, array $value) {
197 $commentKey = '## ';
198 $inArray = in_array($commentKey . $this->localconf_startEditPointToken, $lines);
199 $tokenSet = $this->localconf_startEditPointToken && !$inArray; // Flag is set if the token should be set but is not yet
200 $stopAtToken = $this->localconf_startEditPointToken && $inArray;
201 $comment = 'Modified or inserted by ' . $this->updateIdentity . '.';
202 $format = "%s = %s;\t// " . $comment;
203
204 $insertPos = count($lines);
205 $startPos = 0;
206 if (!($this->localconf_addLinesOnly || $tokenSet)) {
207 for ($i = count($lines) - 1; $i > 0; $i--) {
208 $line = trim($lines[$i]);
209 if ($stopAtToken && t3lib_div::isFirstPartOfStr($line, $this->localconf_startEditPointToken)) {
210 break;
211 }
212 if (t3lib_div::isFirstPartOfStr($line, '?>')) {
213 $insertPos = $i;
214 }
215 if (t3lib_div::isFirstPartOfStr($line, $variable)) {
216 $startPos = $i;
217 break;
218 }
219 }
220 }
221 if ($startPos) {
222 $this->touchedLine = $startPos;
223 $endPos = $startPos;
224 for ($i = $startPos; $i < count($lines); $i++) {
225 $line = trim($lines[$i]);
226 if (t3lib_div::isFirstPartOfStr($line, ');')) {
227 $endPos = $i;
228 break;
229 }
230 }
231
232 $startLines = array_slice($lines, 0, $startPos);
233 $endLines = array_slice($lines, $endPos + 1);
234
235 $lines = $startLines;
236 $definition = $this->array_export($value);
237 $lines[] = sprintf($format, $variable, $definition);
238 foreach ($endLines as $line) {
239 $lines[] = $line;
240 }
241 } else {
242 $lines[$insertPos] = sprintf($format, $variable, $this->array_export($value));
243 $lines[] = '?>';
244 $this->touchedLine = -1;
245 }
246 }
247
248 /**
249 * Returns a parsable string representation of an array variable. This methods enhances
250 * standard method var_export from PHP to take TYPO3's CGL into account.
251 *
252 * @param array $variable
253 * @return string
254 */
255 protected function array_export(array $variable) {
256 $lines = explode("\n", var_export($variable, TRUE));
257 $out = 'array(';
258
259 for ($i = 1; $i < count($lines); $i++) {
260 $out .= "\n";
261 // Make the space-indented declaration tab-indented instead
262 while (substr($lines[$i], 0, 2) === ' ') {
263 $out .= "\t";
264 $lines[$i] = substr($lines[$i], 2);
265 }
266 $out .= $lines[$i];
267 // Array declaration should be next to the assignment and no space between
268 // "array" and its opening parenthesis should exist
269 if (preg_match('/\s=>\s$/', $lines[$i])) {
270 $out .= preg_replace('/^\s*array \(/', 'array(', $lines[$i + 1]);
271 $i++;
272 }
273 }
274
275 return $out;
276 }
277
278 /**
279 * Writes or returns lines from localconf.php
280 *
281 * @param mixed $inlines Array of lines to write back to localconf.php. Possibly
282 * @param string $absFullPath Absolute path of alternative file to use (Notice: this path is not validated in terms of being inside 'TYPO3 space')
283 * @return mixed If $inlines is not an array it will return an array with the lines from localconf.php. Otherwise it will return a status string, either "continue" (updated) or "nochange" (not updated)
284 * @see setValueInLocalconfFile()
285 */
286 function writeToLocalconf_control($inlines = '', $absFullPath = '') {
287 $tmpExt = '.TMP.php';
288 $writeToLocalconf_dat = array();
289 $writeToLocalconf_dat['file'] = $absFullPath ? $absFullPath : PATH_typo3conf . 'localconf.php';
290 $writeToLocalconf_dat['tmpfile'] = $writeToLocalconf_dat['file'] . $tmpExt;
291
292 // Checking write state of localconf.php
293 if (!$this->allowUpdateLocalConf) {
294 throw new RuntimeException(
295 'TYPO3 Fatal Error: ->allowUpdateLocalConf flag in the install object is not set and therefore "localconf.php" cannot be altered.',
296 1270853915
297 );
298 }
299 if (!@is_writable($writeToLocalconf_dat['file'])) {
300 throw new RuntimeException(
301 'TYPO3 Fatal Error: ' . $writeToLocalconf_dat['file'] . ' is not writable!',
302 1270853916
303 );
304 }
305
306 // Splitting localconf.php file into lines
307 $lines = explode(LF, str_replace(CR, '', trim(t3lib_div::getUrl($writeToLocalconf_dat['file']))));
308 $writeToLocalconf_dat['endLine'] = array_pop($lines); // Getting "? >" ending.
309
310 // Checking if "updated" line was set by this tool - if so remove old line.
311 $updatedLine = array_pop($lines);
312 $writeToLocalconf_dat['updatedText'] = '// Updated by ' . $this->updateIdentity . ' ';
313
314 if (!strstr($updatedLine, $writeToLocalconf_dat['updatedText'])) {
315 array_push($lines, $updatedLine);
316 }
317
318 // Setting a line and write
319 if (is_array($inlines)) {
320 // Setting configuration
321 $updatedLine = $writeToLocalconf_dat['updatedText'] . date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' H:i:s');
322 array_push($inlines, $updatedLine);
323 array_push($inlines, $writeToLocalconf_dat['endLine']);
324
325 if ($this->setLocalconf) {
326 $success = $this->writeToLocalconf($inlines, $absFullPath);
327
328 if ($success) {
329 return 'continue';
330 } else {
331 return 'nochange';
332 }
333 } else {
334 return 'nochange';
335 }
336 } else { // Return lines found in localconf.php
337 return $lines;
338 }
339 }
340
341 /**
342 * Writes lines to localconf.php.
343 *
344 * @param array $lines Array of lines to write back to localconf.php
345 * @param string $absFullPath Absolute path of alternative file to use (Notice: this path is not validated in terms of being inside 'TYPO3 space')
346 * @return boolean TRUE if method succeeded, otherwise FALSE
347 */
348 public function writeToLocalconf(array $lines, $absFullPath = '') {
349 $tmpExt = '.TMP.php';
350 $writeToLocalconf_dat = array();
351 $writeToLocalconf_dat['file'] = $absFullPath ? $absFullPath : PATH_typo3conf . 'localconf.php';
352 $writeToLocalconf_dat['tmpfile'] = $writeToLocalconf_dat['file'] . $tmpExt;
353
354 // Checking write state of localconf.php:
355 if (!$this->allowUpdateLocalConf) {
356 throw new RuntimeException(
357 'TYPO3 Fatal Error: ->allowUpdateLocalConf flag in the install object is not set and therefore "localconf.php" cannot be altered.',
358 1270853915
359 );
360 }
361 if (!@is_writable($writeToLocalconf_dat['file'])) {
362 throw new RuntimeException(
363 'TYPO3 Fatal Error: ' . $writeToLocalconf_dat['file'] . ' is not writable!',
364 1270853916
365 );
366 }
367
368 $writeToLocalconf_dat['endLine'] = array_pop($lines); // Getting "? >" ending.
369 if (!strstr('?' . '>', $writeToLocalconf_dat['endLine'])) {
370 $lines[] = $writeToLocalconf_dat['endLine'];
371 $writeToLocalconf_dat['endLine'] = '?' . '>';
372 }
373 // Checking if "updated" line was set by this tool - if so remove old line.
374 $updatedLine = array_pop($lines);
375 $writeToLocalconf_dat['updatedText'] = '// Updated by ' . $this->updateIdentity . ' ';
376
377 if (!strstr($updatedLine, $writeToLocalconf_dat['updatedText'])) {
378 $lines[] = $updatedLine;
379 }
380
381 $updatedLine = $writeToLocalconf_dat['updatedText'] . date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' H:i:s');
382 $lines[] = $updatedLine;
383 $lines[] = $writeToLocalconf_dat['endLine'];
384
385 $success = FALSE;
386 if (!t3lib_div::writeFile($writeToLocalconf_dat['tmpfile'], implode(LF, $lines))) {
387 $msg = 'typo3conf/localconf.php' . $tmpExt . ' could not be written - maybe a write access problem?';
388 } elseif (strcmp(t3lib_div::getUrl($writeToLocalconf_dat['tmpfile']), implode(LF, $lines))) {
389 @unlink($writeToLocalconf_dat['tmpfile']);
390 $msg = 'typo3conf/localconf.php' . $tmpExt . ' was NOT written properly (written content didn\'t match file content) - maybe a disk space problem?';
391 } elseif (!@copy($writeToLocalconf_dat['tmpfile'], $writeToLocalconf_dat['file'])) {
392 $msg = 'typo3conf/localconf.php could not be replaced by typo3conf/localconf.php' . $tmpExt . ' - maybe a write access problem?';
393 } else {
394 @unlink($writeToLocalconf_dat['tmpfile']);
395 $success = TRUE;
396 $msg = 'Configuration written to typo3conf/localconf.php';
397 }
398 $this->messages[] = $msg;
399
400 if (!$success) {
401 t3lib_div::sysLog($msg, 'Core', t3lib_div::SYSLOG_SEVERITY_ERROR);
402 }
403
404 return $success;
405 }
406
407 /**
408 * Checking for linebreaks in the string
409 *
410 * @param string $string String to test
411 * @return boolean Returns TRUE if string is OK
412 * @see setValueInLocalconfFile()
413 */
414 function checkForBadString($string) {
415 return preg_match('/[' . LF . CR . ']/', $string) ? FALSE : TRUE;
416 }
417
418 /**
419 * Replaces ' with \' and \ with \\
420 *
421 * @param string $value Input value
422 * @return string Output value
423 * @see setValueInLocalconfFile()
424 */
425 function slashValueForSingleDashes($value) {
426 $value = str_replace("'.LF.'", '###INSTALL_TOOL_LINEBREAK###', $value);
427 $value = str_replace("'", "\'", str_replace('\\', '\\\\', $value));
428 $value = str_replace('###INSTALL_TOOL_LINEBREAK###', "'.LF.'", $value);
429
430 return $value;
431 }
432
433 /**
434 * Creates a table which checkboxes for updating database.
435 *
436 * @param array $arr Array of statements (key / value pairs where key is used for the checkboxes)
437 * @param string $label Label for the table.
438 * @param boolean $checked If set, then checkboxes are set by default.
439 * @param boolean $iconDis If set, then icons are shown.
440 * @param array $currentValue Array of "current values" for each key/value pair in $arr. Shown if given.
441 * @param boolean $cVfullMsg If set, will show the prefix "Current value" if $currentValue is given.
442 * @return string HTML table with checkboxes for update. Must be wrapped in a form.
443 */
444 function generateUpdateDatabaseForm_checkboxes($arr, $label, $checked = TRUE, $iconDis = FALSE, $currentValue = array(), $cVfullMsg = FALSE) {
445 $out = array();
446 if (is_array($arr)) {
447 $tableId = uniqid('table');
448 if (count($arr) > 1) {
449 $out[] = '
450 <tr class="update-db-fields-batch">
451 <td valign="top">
452 <input type="checkbox" id="' . $tableId . '-checkbox"' . ($checked ? ' checked="checked"' : '') . '
453 onclick="$(\'' . $tableId . '\').select(\'input[type=checkbox]\').invoke(\'setValue\', $(this).checked);" />
454 </td>
455 <td nowrap="nowrap"><label for="' . $tableId . '-checkbox" style="cursor:pointer"><strong>select/deselect all</strong></label></td>
456 </tr>';
457 }
458 foreach ($arr as $key => $string) {
459 $ico = '';
460 $warnings = array();
461
462 if ($iconDis) {
463 if (preg_match('/^TRUNCATE/i', $string)) {
464 $ico .= '<img src="' . $this->backPath . 'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong> </strong>';
465 $warnings['clear_table_info'] = 'Clearing the table is sometimes neccessary when adding new keys. In case of cache_* tables this should not hurt at all. However, use it with care.';
466 } elseif (stristr($string, ' user_')) {
467 $ico .= '<img src="' . $this->backPath . 'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong>(USER) </strong>';
468 } elseif (stristr($string, ' app_')) {
469 $ico .= '<img src="' . $this->backPath . 'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong>(APP) </strong>';
470 } elseif (stristr($string, ' ttx_') || stristr($string, ' tx_')) {
471 $ico .= '<img src="' . $this->backPath . 'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong>(EXT) </strong>';
472 }
473 }
474 $out[] = '
475 <tr>
476 <td valign="top"><input type="checkbox" id="db-' . $key . '" name="' . $this->dbUpdateCheckboxPrefix . '[' . $key . ']" value="1"' . ($checked ? ' checked="checked"' : '') . ' /></td>
477 <td nowrap="nowrap"><label for="db-' . $key . '">' . nl2br($ico . htmlspecialchars($string)) . '</label></td>
478 </tr>';
479 if (isset($currentValue[$key])) {
480 $out[] = '
481 <tr>
482 <td valign="top"></td>
483 <td nowrap="nowrap" style="color:#666666;">' . nl2br((!$cVfullMsg ? 'Current value: ' : '') . '<em>' . $currentValue[$key] . '</em>') . '</td>
484 </tr>';
485 }
486 }
487 if (count($warnings)) {
488 $out[] = '
489 <tr>
490 <td valign="top"></td>
491 <td style="color:#666666;"><em>' . implode('<br />', $warnings) . '</em></td>
492 </tr>';
493 }
494
495 // Compile rows:
496 $content = '
497 <!-- Update database fields / tables -->
498 <h3>' . $label . '</h3>
499 <table border="0" cellpadding="2" cellspacing="2" id="' . $tableId . '" class="update-db-fields">' . implode('', $out) . '
500 </table>';
501 }
502
503 return $content;
504 }
505 }
506 ?>