Fixed bug #10735: Wrong returnUrl with clipboard actions
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_install.php
old mode 100755 (executable)
new mode 100644 (file)
index 00334ef..dc0e89f
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
+*  (c) 1999-2009 Kasper Skaarhoj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -93,6 +93,7 @@ class t3lib_install {
        var $backPath = '../';                          // Backpath (used for icons etc.)
 
        var $multiplySize = 1;                          // Multiplier of SQL field size (for char, varchar and text fields)
+       var $character_sets = array();                  // Caching output of $GLOBALS['TYPO3_DB']->admin_get_charsets()
 
                // Internal, dynamic:
        var $setLocalconf = 0;                          // Used to indicate that a value is change in the line-array of localconf and that it should be written.
@@ -286,61 +287,92 @@ class t3lib_install {
         *************************************/
 
        /**
-        * Reads the field definitions for the input sql-file string
+        * Reads the field definitions for the input SQL-file string
         *
-        * @param       string          Should be a string read from an sql-file made with 'mysqldump [database_name] -d'
+        * @param       string          Should be a string read from an SQL-file made with 'mysqldump [database_name] -d'
         * @return      array           Array with information about table.
         */
        function getFieldDefinitions_fileContent($fileContent)  {
                $lines = t3lib_div::trimExplode(chr(10), $fileContent, 1);
-               $isTable = '';
-               $total = Array();
-
-               foreach($lines as $value)       {
-                       if ($value[0]!='#')     {
-                               if (!$isTable)  {
-                                       $parts = explode(' ',$value);
-                                       if ($parts[0]=='CREATE' && $parts[1]=='TABLE')  {
-                                               $isTable = str_replace( '`', '', $parts[2]);
-                                               if (TYPO3_OS=='WIN') {  // tablenames are always lowercase on windows!
-                                                       $isTable = strtolower($isTable);
+               $table = '';
+               $total = array();
+
+               foreach ($lines as $value) {
+                       if (substr($value,0,1)=='#') {
+                               continue;       // Ignore comments
+                       }
+
+                       if (!strlen($table)) {
+                               $parts = explode(' ',$value);
+                               if ($parts[0]=='CREATE' && $parts[1]=='TABLE')  {
+                                       $table = str_replace( '`', '', $parts[2]);
+                                       if (TYPO3_OS=='WIN') {  // tablenames are always lowercase on windows!
+                                               $table = strtolower($table);
+                                       }
+                               }
+                       } else {
+                               if (substr($value,0,1)==')' && substr($value,-1)==';')  {
+                                       $ttype = array();
+                                       if (preg_match('/(ENGINE|TYPE)[ ]*=[ ]*([a-zA-Z]*)/',$value,$ttype)) {
+                                               $total[$table]['extra']['ENGINE'] = $ttype[2];
+                                       } // Otherwise, just do nothing: If table engine is not defined, just accept the system default.
+
+                                               // Set the collation, if specified
+                                       if (preg_match('/(COLLATE)[ ]*=[ ]*([a-zA-z0-9_-]+)/', $value, $tcollation)) {
+                                               $total[$table]['extra']['COLLATE'] = $tcollation[2];
+                                       } else {
+                                                       // Otherwise, get the CHARACTER SET and try to find the default collation for it as returned by "SHOW CHARACTER SET" query (for details, see http://dev.mysql.com/doc/refman/5.1/en/charset-table.html)
+                                               if (preg_match('/(CHARSET|CHARACTER SET)[ ]*=[ ]*([a-zA-z0-9_-]+)/', $value, $tcharset)) {      // Note: Keywords "DEFAULT CHARSET" and "CHARSET" are the same, so "DEFAULT" can just be ignored
+                                                       $charset = $tcharset[2];
+                                               } else {
+                                                       $charset = $GLOBALS['TYPO3_DB']->default_charset;       // Fallback to default charset
                                                }
+                                               $total[$table]['extra']['COLLATE'] = $this->getCollationForCharset($charset);
                                        }
+
+                                       $table = '';    // Remove table marker and start looking for the next "CREATE TABLE" statement
                                } else {
-                                       if (substr($value,0,1)==')' && substr($value,-1)==';')  {
-                                               $ttype = array();
-                                               preg_match('/(ENGINE|TYPE)=([a-zA-Z]*)/',$value,$ttype);
-                                               $total[$isTable]['extra']['ttype'] = $ttype[2];
-                                               $isTable = '';
-                                       } else {
-                                               $lineV = preg_replace('/,$/','',$value);
-                                               $lineV = str_replace('UNIQUE KEY', 'UNIQUE', $lineV);
-                                               $parts = explode(' ',$lineV,2);
+                                       $lineV = preg_replace('/,$/','',$value);        // Strip trailing commas
+                                       $lineV = str_replace('`', '', $lineV);
+                                       $lineV = str_replace('  ', ' ', $lineV);        // Remove double blanks
+
+                                       $parts = explode(' ', $lineV, 2);
+                                       if (!preg_match('/(PRIMARY|UNIQUE|FULLTEXT|INDEX|KEY)/',$parts[0])) {   // Field definition
 
                                                        // Make sure there is no default value when auto_increment is set
-                                               if(stristr($parts[1],'auto_increment')) {
+                                               if (stristr($parts[1],'auto_increment')) {
                                                        $parts[1] = preg_replace('/ default \'0\'/i','',$parts[1]);
                                                }
                                                        // "default" is always lower-case
-                                               if(strstr($parts[1], ' DEFAULT '))      {
-                                                       $parts[1] = str_replace(' DEFAULT ', ' default ', $parts[1]);
+                                               if (stristr($parts[1], ' DEFAULT '))    {
+                                                       $parts[1] = str_ireplace(' DEFAULT ', ' default ', $parts[1]);
                                                }
 
                                                        // Change order of "default" and "null" statements
                                                $parts[1] = preg_replace('/(.*) (default .*) (NOT NULL)/', '$1 $3 $2', $parts[1]);
                                                $parts[1] = preg_replace('/(.*) (default .*) (NULL)/', '$1 $3 $2', $parts[1]);
 
-                                                       // Remove double blanks
-                                               $parts[1] = preg_replace('/([^ ]+)[ ]+([^ ]+)/', '$1 $2', $parts[1]);
-
-                                               if ($parts[0]!='PRIMARY' && $parts[0]!='KEY' && $parts[0]!='UNIQUE')    {
-                                                       $key = str_replace('`', '', $parts[0]);
-                                                       $total[$isTable]['fields'][$key] = $parts[1];
-                                               } else {        // Process keys
-                                                       $newParts = explode(' ',$parts[1],2);
-                                                       $key = str_replace('`', '', ($parts[0]=='PRIMARY'?$parts[0]:$newParts[0]));
-                                                       $lineV = str_replace('`', '', $lineV);
-                                                       $total[$isTable]['keys'][$key] = $lineV;
+                                               $key = $parts[0];
+                                               $total[$table]['fields'][$key] = $parts[1];
+
+                                       } else {        // Key definition
+                                               $search = array('/UNIQUE (INDEX|KEY)/', '/FULLTEXT (INDEX|KEY)/', '/INDEX/');
+                                               $replace = array('UNIQUE', 'FULLTEXT', 'KEY');
+                                               $lineV = preg_replace($search, $replace, $lineV);
+
+                                               if (preg_match('/PRIMARY|UNIQUE|FULLTEXT/', $parts[0])) {
+                                                       $parts[1] = preg_replace('/^(KEY|INDEX) /', '', $parts[1]);
+                                               }
+
+                                               $newParts = explode(' ',$parts[1],2);
+                                               $key = $parts[0]=='PRIMARY' ? $parts[0] : $newParts[0];
+
+                                               $total[$table]['keys'][$key] = $lineV;
+
+                                                       // This is a protection against doing something stupid: Only allow clearing of cache_* and index_* tables.
+                                               if (preg_match('/^(cache|index)_/',$table)) {
+                                                               // Suggest to truncate (clear) this table
+                                                       $total[$table]['extra']['CLEAR'] = 1;
                                                }
                                        }
                                }
@@ -401,7 +433,7 @@ class t3lib_install {
                                                                                                }
                                                                                                $keys[] = $kfN;
                                                                                        }
-                                                                                       $total[$table]['keys'][$kN] = $match[1].'('.join(',',$keys).')'.$match[3];
+                                                                                       $total[$table]['keys'][$kN] = $match[1].'('.implode(',',$keys).')'.$match[3];
                                                                                }
                                                                        }
                                                                }
@@ -420,6 +452,25 @@ class t3lib_install {
        }
 
        /**
+        * Look up the default collation for specified character set based on "SHOW CHARACTER SET" output
+        *
+        * @param       string          Character set
+        * @return      string          Corresponding default collation
+        */
+       function getCollationForCharset($charset)       {
+                       // Load character sets, if not cached already
+               if (!count($this->character_sets)) {
+                       $this->character_sets = $GLOBALS['TYPO3_DB']->admin_get_charsets();
+               }
+
+               if (isset($this->character_sets[$charset]['Default collation'])) {
+                       $collation = $this->character_sets[$charset]['Default collation'];
+               }
+
+               return $collation;
+       }
+
+       /**
         * Reads the field definitions for the current database
         *
         * @return      array           Array with information about table.
@@ -433,30 +484,50 @@ class t3lib_install {
                echo $GLOBALS['TYPO3_DB']->sql_error();
 
                $tables = $GLOBALS['TYPO3_DB']->admin_get_tables(TYPO3_db);
-               foreach ($tables as $tableName) {
+               foreach ($tables as $tableName => $tableStatus) {
 
                                // Fields:
                        $fieldInformation = $GLOBALS['TYPO3_DB']->admin_get_fields($tableName);
-                       foreach($fieldInformation as $fN => $fieldRow)  {
+                       foreach ($fieldInformation as $fN => $fieldRow) {
                                $total[$tableName]['fields'][$fN] = $this->assembleFieldDefinition($fieldRow);
                        }
 
                                // Keys:
                        $keyInformation = $GLOBALS['TYPO3_DB']->admin_get_keys($tableName);
-                       foreach($keyInformation as $kN => $keyRow)      {
-                               $tempKeys[$tableName][$keyRow['Key_name']][$keyRow['Seq_in_index']] = $keyRow['Column_name'];
-                               if ($keyRow['Sub_part'])        {
-                                       $tempKeys[$tableName][$keyRow['Key_name']][$keyRow['Seq_in_index']].= '('.$keyRow['Sub_part'].')';
+
+                       foreach ($keyInformation as $keyRow) {
+                               $keyName = $keyRow['Key_name'];
+                               $colName = $keyRow['Column_name'];
+                               if ($keyRow['Sub_part']) {
+                                       $colName.= '('.$keyRow['Sub_part'].')';
                                }
-                               if ($keyRow['Key_name']=='PRIMARY')     {
-                                       $tempKeysPrefix[$tableName][$keyRow['Key_name']] = 'PRIMARY KEY';
+                               $tempKeys[$tableName][$keyName][$keyRow['Seq_in_index']] = $colName;
+                               if ($keyName=='PRIMARY') {
+                                       $prefix = 'PRIMARY KEY';
                                } else {
-                                       if ($keyRow['Non_unique'])      {
-                                               $tempKeysPrefix[$tableName][$keyRow['Key_name']] = 'KEY';
+                                       if ($keyRow['Index_type']=='FULLTEXT') {
+                                               $prefix = 'FULLTEXT';
+                                       } elseif ($keyRow['Non_unique']) {
+                                               $prefix = 'KEY';
                                        } else {
-                                               $tempKeysPrefix[$tableName][$keyRow['Key_name']] = 'UNIQUE';
+                                               $prefix = 'UNIQUE';
+                                       }
+                                       $prefix.= ' '.$keyName;
+                               }
+                               $tempKeysPrefix[$tableName][$keyName] = $prefix;
+                       }
+
+                               // Table status (storage engine, collaction, etc.)
+                       if (is_array($tableStatus)) {
+                               $tableExtraFields = array(
+                                       'Engine' => 'ENGINE',
+                                       'Collation' => 'COLLATE',
+                               );
+
+                               foreach ($tableExtraFields as $mysqlKey=>$internalKey) {
+                                       if (isset($tableStatus[$mysqlKey])) {
+                                               $total[$tableName]['extra'][$internalKey] = $tableStatus[$mysqlKey];
                                        }
-                                       $tempKeysPrefix[$tableName][$keyRow['Key_name']].= ' '.$keyRow['Key_name'];
                                }
                        }
                }
@@ -495,16 +566,27 @@ class t3lib_install {
                                                $extraArr[$table] = $info;              // If the table was not in the FDcomp-array, the result array is loaded with that table.
                                                $extraArr[$table]['whole_table']=1;
                                        } else {
-                                               $keyTypes = explode(',','fields,keys');
+                                               $keyTypes = explode(',','extra,fields,keys');
                                                foreach ($keyTypes as $theKey) {
                                                        if (is_array($info[$theKey])) {
                                                                foreach ($info[$theKey] as $fieldN => $fieldC) {
                                                                        $fieldN = str_replace('`','',$fieldN);
-                                                                       if (!isset($FDcomp[$table][$theKey][$fieldN]))  {
+                                                                       if ($fieldN=='COLLATE') {
+                                                                               continue;       // TODO: collation support is currently disabled (needs more testing)
+                                                                       }
+
+                                                                       if (!isset($FDcomp[$table][$theKey][$fieldN])) {
                                                                                $extraArr[$table][$theKey][$fieldN] = $fieldC;
-                                                                       } elseif (strcmp($FDcomp[$table][$theKey][$fieldN], $ignoreNotNullWhenComparing?str_replace(' NOT NULL', '', trim($fieldC)):trim($fieldC)))     {
-                                                                               $diffArr[$table][$theKey][$fieldN] = $fieldC;
-                                                                               $diffArr_cur[$table][$theKey][$fieldN] = $FDcomp[$table][$theKey][$fieldN];
+                                                                       } else {
+                                                                               $fieldC = trim($fieldC);
+                                                                               if ($ignoreNotNullWhenComparing) {
+                                                                                       $fieldC = str_replace(' NOT NULL', '', $fieldC);
+                                                                                       $FDcomp[$table][$theKey][$fieldN] = str_replace(' NOT NULL', '', $FDcomp[$table][$theKey][$fieldN]);
+                                                                               }
+                                                                               if ($fieldC !== $FDcomp[$table][$theKey][$fieldN]) {
+                                                                                       $diffArr[$table][$theKey][$fieldN] = $fieldC;
+                                                                                       $diffArr_cur[$table][$theKey][$fieldN] = $FDcomp[$table][$theKey][$fieldN];
+                                                                               }
                                                                        }
                                                                }
                                                        }
@@ -548,6 +630,18 @@ class t3lib_install {
                                                        if ($info['whole_table']) {
                                                                $whole_table[]=$fN.' '.$fV;
                                                        } else {
+                                                                       // Special case to work around MySQL problems when adding auto_increment fields:
+                                                               if (stristr($fV, 'auto_increment')) {
+                                                                               // The field can only be set "auto_increment" if there exists a PRIMARY key of that field already.
+                                                                               // The check does not look up which field is primary but just assumes it must be the field with the auto_increment value...
+                                                                       if (isset($diffArr['extra'][$table]['keys']['PRIMARY'])) {
+                                                                                       // Remove "auto_increment" from the statement - it will be suggested in a 2nd step after the primary key was created
+                                                                               $fV = str_replace(' auto_increment', '', $fV);
+                                                                       } else {
+                                                                                       // In the next step, attempt to clear the table once again (2 = force)
+                                                                               $info['extra']['CLEAR'] = 2;
+                                                                       }
+                                                               }
                                                                if ($theKey=='extra') {
                                                                        if ($remove) {
                                                                                if (substr($fN,0,strlen($deletedPrefixKey))!=$deletedPrefixKey) {
@@ -591,6 +685,40 @@ class t3lib_install {
                                                        }
                                                }
                                        }
+                                       if (is_array($info['extra'])) {
+                                               $extras = array();
+                                               $extras_currentValue = array();
+                                               $clear_table = false;
+
+                                               foreach ($info['extra'] as $fN => $fV) {
+
+                                                               // Only consider statements which are missing in the database but don't remove existing properties
+                                                       if (!$remove) {
+                                                               if (!$info['whole_table']) {    // If the whole table is created at once, we take care of this later by imploding all elements of $info['extra']
+                                                                       if ($fN=='CLEAR') {
+                                                                                       // Truncate table must happen later, not now
+                                                                                       // Valid values for CLEAR: 1=only clear if keys are missing, 2=clear anyway (force)
+                                                                               if (count($info['keys']) || $fV==2) {
+                                                                                       $clear_table = true;
+                                                                               }
+                                                                               continue;
+                                                                       } else {
+                                                                               $extras[] = $fN.'='.$fV;
+                                                                               $extras_currentValue[] = $fN.'='.$diffArr['diff_currentValues'][$table]['extra'][$fN];
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                               if ($clear_table) {
+                                                       $statement = 'TRUNCATE TABLE '.$table.';';
+                                                       $statements['clear_table'][md5($statement)] = $statement;
+                                               }
+                                               if (count($extras)) {
+                                                       $statement = 'ALTER TABLE '.$table.' '.implode(' ',$extras).';';
+                                                       $statements['change'][md5($statement)] = $statement;
+                                                       $statements['change_currentValue'][md5($statement)] = implode(' ',$extras_currentValue);
+                                               }
+                                       }
                                        if ($info['whole_table']) {
                                                if ($remove) {
                                                        if (substr($table,0,strlen($deletedPrefixKey))!=$deletedPrefixKey) {
@@ -601,12 +729,19 @@ class t3lib_install {
                                                                $statements['drop_table'][md5($statement)] = $statement;
                                                        }
                                                                // count:
-                                                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, '');
-                                                       list($count) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
+                                                       $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', $table);
                                                        $statements['tables_count'][md5($statement)] = $count?'Records in table: '.$count:'';
                                                } else {
                                                        $statement = 'CREATE TABLE '.$table." (\n".implode(",\n",$whole_table)."\n)";
-                                                       $statement .= ($info['extra']['ttype']) ? ' TYPE='.$info['extra']['ttype'].';' : ';';
+                                                       if ($info['extra']) {
+                                                               foreach ($info['extra'] as $k=>$v) {
+                                                                       if ($k=='COLLATE' || $k=='CLEAR') {
+                                                                               continue;       // Skip these special statements. TODO: collation support is currently disabled (needs more testing)
+                                                                       }
+                                                                       $statement.= ' '.$k.'='.$v;     // Add extra attributes like ENGINE, CHARSET, etc.
+                                                               }
+                                                       }
+                                                       $statement.= ';';
                                                        $statements['create_table'][md5($statement)] = $statement;
                                                }
                                        }
@@ -626,10 +761,13 @@ class t3lib_install {
        function assembleFieldDefinition($row)  {
                $field = array($row['Type']);
 
+               if ($row['Null']=='NO') {
+                       $field[] = 'NOT NULL';
+               }
                if (!strstr($row['Type'],'blob') && !strstr($row['Type'],'text')) {
-                               // Add a default value if the field is not auto-incremented (these fields never have a default definition).
+                               // Add a default value if the field is not auto-incremented (these fields never have a default definition)
                        if (!stristr($row['Extra'],'auto_increment')) {
-                               $field[] = 'default '."'".(addslashes($row['Default']))."'";
+                               $field[] = 'default \''.addslashes($row['Default']).'\'';
                        }
                }
                if ($row['Extra']) {
@@ -656,8 +794,10 @@ class t3lib_install {
 
                foreach ($sqlcodeArr as $line => $lineContent) {
                        $is_set = 0;
-                       if(stristr($lineContent,'auto_increment')) {
-                               $lineContent = eregi_replace(' default \'0\'','',$lineContent);
+
+                               // auto_increment fields cannot have a default value!
+                       if (stristr($lineContent,'auto_increment')) {
+                               $lineContent = preg_replace('/ default \'0\'/i', '', $lineContent);
                        }
 
                        if (!$removeNonSQL || (strcmp(trim($lineContent),'') && substr(trim($lineContent),0,1)!='#' && substr(trim($lineContent),0,2)!='--')) {         // '--' is seen as mysqldump comments from server version 3.23.49
@@ -671,10 +811,12 @@ class t3lib_install {
                                        }
                                }
                                $statementArrayPointer++;
+
                        } elseif ($is_set) {
                                $statementArray[$statementArrayPointer].= chr(10);
                        }
                }
+
                return $statementArray;
        }
 
@@ -700,7 +842,7 @@ class t3lib_install {
                                        $sqlLines = explode(chr(10), $lineContent);
                                        foreach ($sqlLines as $k=>$v) {
                                                if (stristr($v,'auto_increment')) {
-                                                       $sqlLines[$k] = eregi_replace(' default \'0\'','',$v);
+                                                       $sqlLines[$k] = preg_replace('/ default \'0\'/i', '', $v);
                                                }
                                        }
                                        $lineContent = implode(chr(10), $sqlLines);
@@ -772,6 +914,9 @@ class t3lib_install {
         */
        function getListOfTables()      {
                $whichTables = $GLOBALS['TYPO3_DB']->admin_get_tables(TYPO3_db);
+               foreach ($whichTables as $key=>&$value) {
+                       $value = $key;
+               }
                return $whichTables;
        }
 
@@ -791,14 +936,17 @@ class t3lib_install {
                if (is_array($arr))     {
                        foreach($arr as $key => $string)        {
                                $ico = '';
+                               $warnings = array();
+
                                if ($iconDis)   {
-                                       if (stristr($string,' user_'))  {
+                                       if (preg_match('/^TRUNCATE/i',$string)) {
+                                               $ico.= '<img src="'.$this->backPath.'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong> </strong>';
+                                               $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.';
+                                       } elseif (stristr($string,' user_'))    {
                                                $ico.= '<img src="'.$this->backPath.'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong>(USER) </strong>';
-                                       }
-                                       if (stristr($string,' app_'))   {
+                                       } elseif (stristr($string,' app_'))     {
                                                $ico.= '<img src="'.$this->backPath.'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong>(APP) </strong>';
-                                       }
-                                       if (stristr($string,' ttx_') || stristr($string,' tx_'))        {
+                                       } elseif (stristr($string,' ttx_') || stristr($string,' tx_'))  {
                                                $ico.= '<img src="'.$this->backPath.'gfx/icon_warning.gif" width="18" height="16" align="top" alt="" /><strong>(EXT) </strong>';
                                        }
                                }
@@ -815,6 +963,13 @@ class t3lib_install {
                                        </tr>';
                                }
                        }
+                       if (count($warnings)) {
+                               $out[] = '
+                                       <tr>
+                                               <td valign="top"></td>
+                                               <td style="color : #666666;"><em>' . implode('<br />',$warnings) . '</em></td>
+                                       </tr>';
+                       }
 
                        // Compile rows:
                        $content = '
@@ -842,4 +997,5 @@ class t3lib_install {
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_install.php'])  {
        include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_install.php']);
 }
-?>
+
+?>
\ No newline at end of file