/typo3/sysext/dbal/class.tx_dbal_em.php
PHP | 392 lines | 215 code | 36 blank | 141 comment | 38 complexity | ba7915e1636cbb7ed16382d0ae1ca72a MD5 | raw file
Possible License(s): Apache-2.0, BSD-2-Clause, LGPL-3.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: class.tx_dbal_em.php 40828 2010-12-05 14:55:53Z xperseguers $
- *
- * @author Xavier Perseguers <typo3@perseguers.ch>
- *
- * @package TYPO3
- * @subpackage dbal
- */
- class tx_dbal_em implements tx_em_Index_CheckDatabaseUpdatesHook {
- /**
- * Maximal length for an identifier in Oracle.
- *
- * @var integer
- */
- protected $maxIdentifierLength = 30;
- /**
- * Table names should be short enough in order to let ADOdb generates
- * the corresponding sequence for the auto-increment field 'uid'.
- * That is, a sequence of the form {table}_uid
- *
- * @var integer
- */
- protected $tableNameCharacterReservation = 4;
- /**
- * 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 t3lib_install $instObj: Instance of the installer
- * @param SC_mod_tools_em_index $parent: The calling parent object
- * @return string Either empty string or a pre-processing user form
- */
- public function preProcessDatabaseUpdates($extKey, array $extInfo, array $diff, t3lib_install $instObj, 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'), $instObj);
- }
- // 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 ($field !== 'PRIMARY' && $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, $extInfo, $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 === '') {
- if (substr($table, -3) === '_mm') {
- $needsRemapping = strlen($table) > $this->maxIdentifierLength;
- } else {
- $needsRemapping = strlen($table) > $this->maxIdentifierLength - $this->tableNameCharacterReservation;
- }
- } 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 $extInfo
- * @param array $tables
- * @param array $fields
- * @return array
- * @api
- */
- public function getMappingSuggestions($extKey, array $extInfo, array $tables, array $fields) {
- $suggestions = array();
- switch ($extKey) {
- case 'direct_mail':
- $suggestions['sys_dmail_ttaddress_category_mm'] = array(
- 'mapTableName' => 'sys_dmail_ttaddress_cat_mm',
- );
- $suggestions['sys_dmail_ttcontent_category_mm'] = array(
- 'mapTableName' => 'sys_dmail_ttcontent_cat_mm',
- );
- break;
- case 'extbase':
- $suggestions['tx_extbase_cache_reflection_tags'] = array(
- 'mapTableName' => 'tx_extbase_cache_reflect_tags',
- );
- break;
- case 'templavoila':
- $suggestions['tx_templavoila_datastructure'] = array(
- 'mapTableName' => 'tx_templavoila_ds',
- );
- $suggestions['tx_templavoila_tmplobj'] = array(
- 'mapTableName' => 'tx_templavoila_tmpl',
- );
- break;
- default:
- $dependencies = array_keys($extInfo['EM_CONF']['constraints']['depends']);
- if (t3lib_div::inArray($dependencies, 'extbase')) {
- $this->storeExtbaseMappingSuggestions($suggestions, $extKey, $extInfo, $tables, $fields);
- }
- }
- // Existing mapping take precedence over suggestions
- $suggestions = t3lib_div::array_merge_recursive_overrule($suggestions, $this->mapping);
- return $suggestions;
- }
- /**
- * Stores suggestions for the mapping of table/field names for an Extbase-based extension.
- *
- * @param array &$suggestions
- * @param string $extKey
- * @param array $extInfo
- * @param array $tables
- * @param array $fields
- * @return void
- */
- protected function storeExtbaseMappingSuggestions(array &$suggestions, $extKey, array $extInfo, array $tables, array $fields) {
- foreach ($tables as $table) {
- // Remove the "domain_model" part of the table name
- $suggestions[$table] = array(
- 'mapTableName' => str_replace('domain_model_', '', $table),
- );
- }
- }
- /**
- * 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 table names are longer than %s characters and/or some field names are longer than %s characters.'
- . ' This is incompatible with your database:'
- . ' <ul style="list-style: square; margin: 3px 1em; padding: 3px 1em;">'
- . ' <li>Table names should be short enough to let ADOdb generates a sequence of the form {table}_uid for the'
- . ' auto-increment "uid" field within %s characters;</li>'
- . ' <li>Field names may not contain more than %s characters.</li>'
- . ' </ul>',
- $this->maxIdentifierLength - $this->tableNameCharacterReservation,
- $this->maxIdentifierLength,
- $this->maxIdentifierLength,
- $this->maxIdentifierLength
- );
- $tables = array_unique(array_merge($tables, array_keys($fields)));
- foreach ($tables as $table) {
- $newTableName = $table;
- if (isset($suggestions[$table]) && isset($suggestions[$table]['mapTableName'])) {
- $newTableName = $suggestions[$table]['mapTableName'];
- }
- $out[] = '
- <tr>
- <td style="padding-top: 1em;"><label for="table-' . $table . '">' . $table . '</label></td>
- <td style="padding-top: 1em;">=></td>
- <td style="padding-top: 1em;"><input type="text" size="35" 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> <label for="field-' . $table . '_' . $field . '">' . $field . '</label></td>
- <td>=></td>
- <td><input type="text" size="35" 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
- * @param t3lib_install $instObj
- * @return void
- * @api
- */
- public function updateMapping(array $data, t3lib_install $instObj) {
- $newMapping = $this->mapping;
- foreach ($data['tables'] as $table => $newName) {
- $newName = trim($newName);
- if ($newName && $newName !== $table) {
- 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 && $newName !== $field) {
- if (!isset($newMapping[$table])) {
- $newMapping[$table] = array();
- }
- if (!isset($newMapping[$table]['mapFieldNames'])) {
- $newMapping[$table]['mapFieldNames'] = array();
- }
- $newMapping[$table]['mapFieldNames'][$field] = $newName;
- }
- }
- }
- }
- // Sort table and field names
- foreach ($newMapping as $table => &$config) {
- if (isset($config['mapFieldNames'])) {
- ksort($config['mapFieldNames']);
- }
- }
- ksort($newMapping);
- // Write new mapping to localconf.php
- $key = '$TYPO3_CONF_VARS[\'EXTCONF\'][\'dbal\'][\'mapping\']';
- $instObj->allowUpdateLocalConf = 1;
- $instObj->updateIdentity = 'TYPO3 Extension Manager';
- $lines = $instObj->writeToLocalconf_control();
- $instObj->setArrayValueInLocalconfFile($lines, $key, $newMapping);
- if ($instObj->writeToLocalconf($lines)) {
- $this->mapping = $newMapping;
- }
- }
- }
- if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/dbal/class.tx_dbal_em.php'])) {
- include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/dbal/class.tx_dbal_em.php']);
- }
- ?>