Project

General

Profile

Bug #23575 » 15755.diff

Administrator Admin, 2010-10-31 16:38

View differences:

class.tx_dbal_em.php (revision 0)
<?php
/***************************************************************
* Copyright notice
*
* (c) 2010 Xavier Perseguers <typo3@perseguers.ch>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
* Hooks for TYPO3 Extension Manager.
*
* $Id$
*
* @author Xavier Perseguers <typo3@perseguers.ch>
*
* @package TYPO3
* @subpackage dbal
*/
class tx_dbal_em implements em_index_checkDBupdatesHook {
/**
* Maximal length for an identifier in Oracle.
*
* @var integer
*/
protected $maxIdentifierLength = 30;
/**
* Mapping of table and field names.
*
* @var array
*/
protected $mapping;
/**
* Initializes internal variables.
*
* @return void
*/
protected function init() {
$mapping = @$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'];
if (!$mapping) {
$mapping = array();
}
$this->mapping = $mapping;
}
/**
* Hook that allows pre-processing of database structure modifications.
* This returns a user form that will temporarily replace the standard
* database update form to let user configure mapping.
*
* @param string $extKey: Extension key
* @param array $extInfo: Extension information array
* @param array $diff: Database differences
* @param SC_mod_tools_em_index $parent: The calling parent object
* @return string Either empty string or a pre-processing user form
*/
public function preprocessDBupdates($extKey, array $extInfo, array $diff, SC_mod_tools_em_index $parent) {
$content = '';
// Remapping is only mandatory for Oracle:
if ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['handlerCfg']['_DEFAULT']['type'] !== 'adodb'
&& $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['handlerCfg']['_DEFAULT']['config']['driver'] !== 'oci8') {
// Not using Oracle
return '';
}
$this->init();
if (t3lib_div::GPvar('dbal')) {
$this->updateMapping(t3lib_div::GPvar('dbal'));
}
// Search all table and field names which should be remapped
$tableCandidates = array();
$fieldsCandidates = array();
foreach ($diff['extra'] as $table => $config) {
if ($this->needsMapping($table)) {
$tableCandidates[] = $table;
}
foreach ($config['fields'] as $field => $type) {
if ($this->needsMapping($table, $field)) {
if (!isset($fieldsCandidates[$table])) {
$fieldsCandidates[$table] = array();
}
$fieldsCandidates[$table][$field] = array(
'fullName' => $field,
);
}
}
if (!isset($config['keys'])) {
continue; // Process next table
}
foreach ($config['keys'] as $field => $def) {
if ($this->needsMapping($table, $field, TRUE)) {
if (!t3lib_div::inArray($tableCandidates, $table)) {
$tableCandidates[] = $table;
}
if (!isset($fieldsCandidates[$table])) {
$fieldsCandidates[$table] = array();
}
$fieldsCandidates[$table][$field] = array(
'fullName' => $table . '_' . $field,
);
}
}
}
if ($tableCandidates || $fieldsCandidates) {
$mappingSuggestions = $this->getMappingSuggestions($extKey, $tableCandidates, $fieldsCandidates);
$content .= $this->generateMappingForm($tableCandidates, $fieldsCandidates, $mappingSuggestions);
}
return $content;
}
/**
* Returns TRUE if either the table or the field name should be remapped.
*
* @param string $table
* @param string $field
* @param boolean $isKeyField
* @return boolean TRUE if mapping is needed, otherwise FALSE
*/
protected function needsMapping($table, $field = '', $isKeyField = FALSE) {
$needsRemapping = FALSE;
// Take existing DBAL mapping into account
$origTable = $table;
if (isset($this->mapping[$origTable])) {
if (isset($this->mapping[$origTable]['mapTableName'])) {
$table = $this->mapping[$origTable]['mapTableName'];
}
if ($field !== '' && isset($this->mapping[$origTable]['mapFieldNames'])) {
if (isset($this->mapping[$origTable]['mapFieldNames'][$field])) {
$field = $this->mapping[$origTable]['mapFieldNames'][$field];
}
}
}
if ($field === '') {
$needsRemapping = strlen($table) > $this->maxIdentifierLength;
} elseif (!$isKeyField) {
$needsRemapping = strlen($field) > $this->maxIdentifierLength;
} else {
$needsRemapping = strlen($table . '_' . $field) > $this->maxIdentifierLength;
}
return $needsRemapping;
}
/**
* Returns suggestions for the mapping of table/field names.
*
* @param string $extKey
* @param array $tables
* @param array $fields
* @return array
* @api
*/
public function getMappingSuggestions($extKey, array $tables, array $fields) {
$suggestions = array();
switch ($extKey) {
case 'static_info_tables':
$suggestions['tx_staticinfotables_hotlist'] = array(
'mapTableName' => 'tx_staticinfotables_hotlst',
'mapFieldNames' => array(
'uid_local' => 'uid',
),
);
break;
case 'templavoila':
//$suggestions[$extKey]
break;
}
// Existing mapping take precedence over suggestions
$suggestions = t3lib_div::array_merge_recursive_overrule($suggestions, $this->mapping);
return $suggestions;
}
/**
* Generates a mapping form.
*
* @param array $tables
* @param array $fields
* @param array $suggestions
* @return string
*/
protected function generateMappingForm(array $tables, array $fields, array $suggestions) {
$out = array();
$tableId = uniqid('table');
$label = 'DBAL Mapping';
$description = sprintf('Some tables and/or fields are longer than %s characters. As this is incompatible with '
. 'your database, you need to remap them to shorter identifiers.', $this->maxIdentifierLength);
$tables = array_unique(array_merge($tables, array_keys($fields)));
foreach ($tables as $table) {
$newTableName = $table;
if (isset($suggestions[$table])) {
$newTableName = $suggestions[$table]['mapTableName'];
}
$out[] = '
<tr>
<td><label for="table-' . $table . '">' . $table . '</label></td>
<td>=&gt;</td>
<td><input type="text" size="30" id="table-' . $table . '" name="dbal[tables][' . $table . ']" value="' . $newTableName . '" /> '
. strlen($newTableName) . ' characters'
. '</td>
</tr>';
if (isset($fields[$table])) {
foreach ($fields[$table] as $field => $info) {
$newFieldName = $field;
if (isset($suggestions[$table]) && isset($suggestions[$table]['mapFieldNames'])) {
if (isset($suggestions[$table]['mapFieldNames'][$field])) {
$newFieldName = $suggestions[$table]['mapFieldNames'][$field];
}
}
$newFieldFullName = preg_replace('/^' . $table . '/', $newTableName, $info['fullName']);
$newFieldFullName = preg_replace('/' . $field . '$/', $newFieldName, $newFieldFullName);
$out[] = '
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;<label for="field-' . $table . '_' . $field . '">' . $field . '</label></td>
<td>=&gt;</td>
<td><input type="text" size="30" id="field-' . $table . '_' . $field . '" name="dbal[fields][' . $table . '][' . $field . ']" value="' . $newFieldName . '" /> '
. ($info['fullname'] !== $field ? strlen($newFieldFullName) . ' characters: ' . $newFieldFullName : '')
. '</td>
</tr>';
}
}
}
// Compile rows:
$content = '
<!-- Remapping database fields / tables -->
<h3>' . $label . '</h3>
<p>' . $description . '</p>
<table border="0" cellpadding="2" cellspacing="2" id="' . $tableId . '" class="remap-db-table-fields">' . implode('', $out) . '
</table>';
return $content;
}
/**
* Updates the mapping in localconf.php according to form input values.
*
* @param array $data
* @return void
* @api
*/
public function updateMapping(array $data) {
$newMapping = $this->mapping;
foreach ($data['tables'] as $table => $newName) {
$newName = trim($newName);
if ($newName) {
if (!isset($newMapping[$table])) {
$newMapping[$table] = array();
}
$newMapping[$table]['mapTableName'] = $newName;
}
if (isset($data['fields'][$table])) {
foreach ($data['fields'][$table] as $field => $newName) {
$newName = trim($newName);
if ($newName) {
if (!isset($newMapping[$table])) {
$newMapping[$table] = array();
}
if (!isset($newMapping[$table]['mapFieldNames'])) {
$newMapping[$table]['mapFieldNames'] = array();
}
$newMapping[$table]['mapFieldNames'][$field] = $newName;
}
}
}
}
// Write new mapping to localconf.php
$key = '$TYPO3_CONF_VARS[\'EXTCONF\'][\'dbal\'][\'mapping\']';
if ($this->writeToLocalconf($key, $newMapping)) {
$this->mapping = $newMapping;
}
}
/**
* Writes an array value configuration option to localconf.php.
* We don't use the <code>tx_install</code> methods as they do not
* support multiple line configuration options.
*
* @param string $key
* @param array $arr
* @return boolean
*/
protected function writeToLocalconf($key, array $arr) {
$localconfFile = PATH_site . 'typo3conf/localconf.php';
$lines = explode("\n", file_get_contents($localconfFile));
$marker = '## INSTALL SCRIPT EDIT POINT TOKEN';
$format = "%s = %s;\t// Updated by TYPO3 Extension Manager " . date('d-m-Y');
$insertPos = count($lines);
$startPos = 0;
for ($i = count($lines) - 1; $i > 0 && !t3lib_div::isFirstPartOfStr($lines[$i], $marker); $i--) {
if (t3lib_div::isFirstPartOfStr($lines[$i], '?>')) {
$insertPos = $i;
}
if (t3lib_div::isFirstPartOfStr($lines[$i], $key)) {
$startPos = $i;
break;
}
}
if ($startPos) {
$endPos = $startPos;
for ($i = $startPos; $i < count($lines); $i++) {
if (t3lib_div::isFirstPartOfStr($lines[$i], ');')) {
$endPos = $i;
break;
}
}
$startLines = array_slice($lines, 0, $startPos);
$endLines = array_slice($lines, $endPos + 1);
$lines = $startLines;
$lines[] = sprintf($format, $key, var_export($arr, TRUE));
foreach ($endLines as $line) {
$lines[] = $line;
}
} else {
$lines[$insertPos] = sprintf($format, $key, var_export($arr, TRUE));
$lines[] = '?>';
}
return t3lib_div::writeFile($localconfFile, implode("\n", $lines));
}
}
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dbal/class.tx_dbal_em.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dbal/class.tx_dbal_em.php']);
}
?>
ext_localconf.php (working copy)
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install/mod/class.tx_install.php']['stepOutput'][] = 'EXT:dbal/class.tx_dbal_installtool.php:tx_dbal_installtool';
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install/mod/class.tx_install.php']['writeLocalconf'][] = 'EXT:dbal/class.tx_dbal_installtool.php:tx_dbal_installtool';
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install/mod/class.tx_install.php']['requiredPhpModules'][] = 'EXT:dbal/class.tx_dbal_installtool.php:tx_dbal_installtool';
// Register a hook for the extension manager
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/mod/tools/em/index.php']['checkDBupdates'][] = 'EXT:dbal/class.tx_dbal_em.php:tx_dbal_em';
?>
(2-2/4)