PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/typo3/sysext/dbal/class.tx_dbal_em.php

https://github.com/andreaswolf/typo3-tceforms
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
  1. <?php
  2. /***************************************************************
  3. * Copyright notice
  4. *
  5. * (c) 2010 Xavier Perseguers <typo3@perseguers.ch>
  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. * Hooks for TYPO3 Extension Manager.
  29. *
  30. * $Id: class.tx_dbal_em.php 40828 2010-12-05 14:55:53Z xperseguers $
  31. *
  32. * @author Xavier Perseguers <typo3@perseguers.ch>
  33. *
  34. * @package TYPO3
  35. * @subpackage dbal
  36. */
  37. class tx_dbal_em implements tx_em_Index_CheckDatabaseUpdatesHook {
  38. /**
  39. * Maximal length for an identifier in Oracle.
  40. *
  41. * @var integer
  42. */
  43. protected $maxIdentifierLength = 30;
  44. /**
  45. * Table names should be short enough in order to let ADOdb generates
  46. * the corresponding sequence for the auto-increment field 'uid'.
  47. * That is, a sequence of the form {table}_uid
  48. *
  49. * @var integer
  50. */
  51. protected $tableNameCharacterReservation = 4;
  52. /**
  53. * Mapping of table and field names.
  54. *
  55. * @var array
  56. */
  57. protected $mapping;
  58. /**
  59. * Initializes internal variables.
  60. *
  61. * @return void
  62. */
  63. protected function init() {
  64. $mapping = @$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'];
  65. if (!$mapping) {
  66. $mapping = array();
  67. }
  68. $this->mapping = $mapping;
  69. }
  70. /**
  71. * Hook that allows pre-processing of database structure modifications.
  72. * This returns a user form that will temporarily replace the standard
  73. * database update form to let user configure mapping.
  74. *
  75. * @param string $extKey: Extension key
  76. * @param array $extInfo: Extension information array
  77. * @param array $diff: Database differences
  78. * @param t3lib_install $instObj: Instance of the installer
  79. * @param SC_mod_tools_em_index $parent: The calling parent object
  80. * @return string Either empty string or a pre-processing user form
  81. */
  82. public function preProcessDatabaseUpdates($extKey, array $extInfo, array $diff, t3lib_install $instObj, SC_mod_tools_em_index $parent) {
  83. $content = '';
  84. // Remapping is only mandatory for Oracle:
  85. if ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['handlerCfg']['_DEFAULT']['type'] !== 'adodb'
  86. && $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['handlerCfg']['_DEFAULT']['config']['driver'] !== 'oci8') {
  87. // Not using Oracle
  88. return '';
  89. }
  90. $this->init();
  91. if (t3lib_div::GPvar('dbal')) {
  92. $this->updateMapping(t3lib_div::GPvar('dbal'), $instObj);
  93. }
  94. // Search all table and field names which should be remapped
  95. $tableCandidates = array();
  96. $fieldsCandidates = array();
  97. foreach ($diff['extra'] as $table => $config) {
  98. if ($this->needsMapping($table)) {
  99. $tableCandidates[] = $table;
  100. }
  101. foreach ($config['fields'] as $field => $type) {
  102. if ($this->needsMapping($table, $field)) {
  103. if (!isset($fieldsCandidates[$table])) {
  104. $fieldsCandidates[$table] = array();
  105. }
  106. $fieldsCandidates[$table][$field] = array(
  107. 'fullName' => $field,
  108. );
  109. }
  110. }
  111. /*
  112. if (!isset($config['keys'])) {
  113. continue; // Process next table
  114. }
  115. foreach ($config['keys'] as $field => $def) {
  116. if ($field !== 'PRIMARY' && $this->needsMapping($table, $field, TRUE)) {
  117. if (!t3lib_div::inArray($tableCandidates, $table)) {
  118. $tableCandidates[] = $table;
  119. }
  120. if (!isset($fieldsCandidates[$table])) {
  121. $fieldsCandidates[$table] = array();
  122. }
  123. $fieldsCandidates[$table][$field] = array(
  124. 'fullName' => $table . '_' . $field,
  125. );
  126. }
  127. }
  128. */
  129. }
  130. if ($tableCandidates || $fieldsCandidates) {
  131. $mappingSuggestions = $this->getMappingSuggestions($extKey, $extInfo, $tableCandidates, $fieldsCandidates);
  132. $content .= $this->generateMappingForm($tableCandidates, $fieldsCandidates, $mappingSuggestions);
  133. }
  134. return $content;
  135. }
  136. /**
  137. * Returns TRUE if either the table or the field name should be remapped.
  138. *
  139. * @param string $table
  140. * @param string $field
  141. * @param boolean $isKeyField
  142. * @return boolean TRUE if mapping is needed, otherwise FALSE
  143. */
  144. protected function needsMapping($table, $field = '', $isKeyField = FALSE) {
  145. $needsRemapping = FALSE;
  146. // Take existing DBAL mapping into account
  147. $origTable = $table;
  148. if (isset($this->mapping[$origTable])) {
  149. if (isset($this->mapping[$origTable]['mapTableName'])) {
  150. $table = $this->mapping[$origTable]['mapTableName'];
  151. }
  152. if ($field !== '' && isset($this->mapping[$origTable]['mapFieldNames'])) {
  153. if (isset($this->mapping[$origTable]['mapFieldNames'][$field])) {
  154. $field = $this->mapping[$origTable]['mapFieldNames'][$field];
  155. }
  156. }
  157. }
  158. if ($field === '') {
  159. if (substr($table, -3) === '_mm') {
  160. $needsRemapping = strlen($table) > $this->maxIdentifierLength;
  161. } else {
  162. $needsRemapping = strlen($table) > $this->maxIdentifierLength - $this->tableNameCharacterReservation;
  163. }
  164. } elseif (!$isKeyField) {
  165. $needsRemapping = strlen($field) > $this->maxIdentifierLength;
  166. } else {
  167. $needsRemapping = strlen($table . '_' . $field) > $this->maxIdentifierLength;
  168. }
  169. return $needsRemapping;
  170. }
  171. /**
  172. * Returns suggestions for the mapping of table/field names.
  173. *
  174. * @param string $extKey
  175. * @param array $extInfo
  176. * @param array $tables
  177. * @param array $fields
  178. * @return array
  179. * @api
  180. */
  181. public function getMappingSuggestions($extKey, array $extInfo, array $tables, array $fields) {
  182. $suggestions = array();
  183. switch ($extKey) {
  184. case 'direct_mail':
  185. $suggestions['sys_dmail_ttaddress_category_mm'] = array(
  186. 'mapTableName' => 'sys_dmail_ttaddress_cat_mm',
  187. );
  188. $suggestions['sys_dmail_ttcontent_category_mm'] = array(
  189. 'mapTableName' => 'sys_dmail_ttcontent_cat_mm',
  190. );
  191. break;
  192. case 'extbase':
  193. $suggestions['tx_extbase_cache_reflection_tags'] = array(
  194. 'mapTableName' => 'tx_extbase_cache_reflect_tags',
  195. );
  196. break;
  197. case 'templavoila':
  198. $suggestions['tx_templavoila_datastructure'] = array(
  199. 'mapTableName' => 'tx_templavoila_ds',
  200. );
  201. $suggestions['tx_templavoila_tmplobj'] = array(
  202. 'mapTableName' => 'tx_templavoila_tmpl',
  203. );
  204. break;
  205. default:
  206. $dependencies = array_keys($extInfo['EM_CONF']['constraints']['depends']);
  207. if (t3lib_div::inArray($dependencies, 'extbase')) {
  208. $this->storeExtbaseMappingSuggestions($suggestions, $extKey, $extInfo, $tables, $fields);
  209. }
  210. }
  211. // Existing mapping take precedence over suggestions
  212. $suggestions = t3lib_div::array_merge_recursive_overrule($suggestions, $this->mapping);
  213. return $suggestions;
  214. }
  215. /**
  216. * Stores suggestions for the mapping of table/field names for an Extbase-based extension.
  217. *
  218. * @param array &$suggestions
  219. * @param string $extKey
  220. * @param array $extInfo
  221. * @param array $tables
  222. * @param array $fields
  223. * @return void
  224. */
  225. protected function storeExtbaseMappingSuggestions(array &$suggestions, $extKey, array $extInfo, array $tables, array $fields) {
  226. foreach ($tables as $table) {
  227. // Remove the "domain_model" part of the table name
  228. $suggestions[$table] = array(
  229. 'mapTableName' => str_replace('domain_model_', '', $table),
  230. );
  231. }
  232. }
  233. /**
  234. * Generates a mapping form.
  235. *
  236. * @param array $tables
  237. * @param array $fields
  238. * @param array $suggestions
  239. * @return string
  240. */
  241. protected function generateMappingForm(array $tables, array $fields, array $suggestions) {
  242. $out = array();
  243. $tableId = uniqid('table');
  244. $label = 'DBAL Mapping';
  245. $description = sprintf('Some table names are longer than %s characters and/or some field names are longer than %s characters.'
  246. . ' This is incompatible with your database:'
  247. . ' <ul style="list-style: square; margin: 3px 1em; padding: 3px 1em;">'
  248. . ' <li>Table names should be short enough to let ADOdb generates a sequence of the form {table}_uid for the'
  249. . ' auto-increment "uid" field within %s characters;</li>'
  250. . ' <li>Field names may not contain more than %s characters.</li>'
  251. . ' </ul>',
  252. $this->maxIdentifierLength - $this->tableNameCharacterReservation,
  253. $this->maxIdentifierLength,
  254. $this->maxIdentifierLength,
  255. $this->maxIdentifierLength
  256. );
  257. $tables = array_unique(array_merge($tables, array_keys($fields)));
  258. foreach ($tables as $table) {
  259. $newTableName = $table;
  260. if (isset($suggestions[$table]) && isset($suggestions[$table]['mapTableName'])) {
  261. $newTableName = $suggestions[$table]['mapTableName'];
  262. }
  263. $out[] = '
  264. <tr>
  265. <td style="padding-top: 1em;"><label for="table-' . $table . '">' . $table . '</label></td>
  266. <td style="padding-top: 1em;">=&gt;</td>
  267. <td style="padding-top: 1em;"><input type="text" size="35" id="table-' . $table . '" name="dbal[tables][' . $table . ']" value="' . $newTableName . '" /> '
  268. . strlen($newTableName) . ' characters'
  269. . '</td>
  270. </tr>';
  271. if (isset($fields[$table])) {
  272. foreach ($fields[$table] as $field => $info) {
  273. $newFieldName = $field;
  274. if (isset($suggestions[$table]) && isset($suggestions[$table]['mapFieldNames'])) {
  275. if (isset($suggestions[$table]['mapFieldNames'][$field])) {
  276. $newFieldName = $suggestions[$table]['mapFieldNames'][$field];
  277. }
  278. }
  279. $newFieldFullName = preg_replace('/^' . $table . '/', $newTableName, $info['fullName']);
  280. $newFieldFullName = preg_replace('/' . $field . '$/', $newFieldName, $newFieldFullName);
  281. $out[] = '
  282. <tr>
  283. <td>&nbsp;&nbsp;&nbsp;&nbsp;<label for="field-' . $table . '_' . $field . '">' . $field . '</label></td>
  284. <td>=&gt;</td>
  285. <td><input type="text" size="35" id="field-' . $table . '_' . $field . '" name="dbal[fields][' . $table . '][' . $field . ']" value="' . $newFieldName . '" /> '
  286. . ($info['fullname'] !== $field ? strlen($newFieldFullName) . ' characters: ' . $newFieldFullName : '')
  287. . '</td>
  288. </tr>';
  289. }
  290. }
  291. }
  292. // Compile rows:
  293. $content = '
  294. <!-- Remapping database fields / tables -->
  295. <h3>' . $label . '</h3>
  296. <p>' . $description . '</p>
  297. <table border="0" cellpadding="2" cellspacing="2" id="' . $tableId . '" class="remap-db-table-fields">' . implode('', $out) . '
  298. </table>';
  299. return $content;
  300. }
  301. /**
  302. * Updates the mapping in localconf.php according to form input values.
  303. *
  304. * @param array $data
  305. * @param t3lib_install $instObj
  306. * @return void
  307. * @api
  308. */
  309. public function updateMapping(array $data, t3lib_install $instObj) {
  310. $newMapping = $this->mapping;
  311. foreach ($data['tables'] as $table => $newName) {
  312. $newName = trim($newName);
  313. if ($newName && $newName !== $table) {
  314. if (!isset($newMapping[$table])) {
  315. $newMapping[$table] = array();
  316. }
  317. $newMapping[$table]['mapTableName'] = $newName;
  318. }
  319. if (isset($data['fields'][$table])) {
  320. foreach ($data['fields'][$table] as $field => $newName) {
  321. $newName = trim($newName);
  322. if ($newName && $newName !== $field) {
  323. if (!isset($newMapping[$table])) {
  324. $newMapping[$table] = array();
  325. }
  326. if (!isset($newMapping[$table]['mapFieldNames'])) {
  327. $newMapping[$table]['mapFieldNames'] = array();
  328. }
  329. $newMapping[$table]['mapFieldNames'][$field] = $newName;
  330. }
  331. }
  332. }
  333. }
  334. // Sort table and field names
  335. foreach ($newMapping as $table => &$config) {
  336. if (isset($config['mapFieldNames'])) {
  337. ksort($config['mapFieldNames']);
  338. }
  339. }
  340. ksort($newMapping);
  341. // Write new mapping to localconf.php
  342. $key = '$TYPO3_CONF_VARS[\'EXTCONF\'][\'dbal\'][\'mapping\']';
  343. $instObj->allowUpdateLocalConf = 1;
  344. $instObj->updateIdentity = 'TYPO3 Extension Manager';
  345. $lines = $instObj->writeToLocalconf_control();
  346. $instObj->setArrayValueInLocalconfFile($lines, $key, $newMapping);
  347. if ($instObj->writeToLocalconf($lines)) {
  348. $this->mapping = $newMapping;
  349. }
  350. }
  351. }
  352. if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/dbal/class.tx_dbal_em.php'])) {
  353. include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/dbal/class.tx_dbal_em.php']);
  354. }
  355. ?>