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