TableSearch.class.php 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Handles Table search and Zoom search
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. /**
  12. * Class to handle normal-search
  13. * and zoom-search in a table
  14. *
  15. * @package PhpMyAdmin
  16. */
  17. class PMA_TableSearch
  18. {
  19. /**
  20. * Database name
  21. *
  22. * @access private
  23. * @var string
  24. */
  25. private $_db;
  26. /**
  27. * Table name
  28. *
  29. * @access private
  30. * @var string
  31. */
  32. private $_table;
  33. /**
  34. * Normal search or Zoom search
  35. *
  36. * @access private
  37. * @var string
  38. */
  39. private $_searchType;
  40. /**
  41. * Names of columns
  42. *
  43. * @access private
  44. * @var array
  45. */
  46. private $_columnNames;
  47. /**
  48. * Types of columns
  49. *
  50. * @access private
  51. * @var array
  52. */
  53. private $_columnTypes;
  54. /**
  55. * Collations of columns
  56. *
  57. * @access private
  58. * @var array
  59. */
  60. private $_columnCollations;
  61. /**
  62. * Null Flags of columns
  63. *
  64. * @access private
  65. * @var array
  66. */
  67. private $_columnNullFlags;
  68. /**
  69. * Whether a geometry column is present
  70. *
  71. * @access private
  72. * @var boolean
  73. */
  74. private $_geomColumnFlag;
  75. /**
  76. * Foreign Keys
  77. *
  78. * @access private
  79. * @var array
  80. */
  81. private $_foreigners;
  82. /**
  83. * Public Constructor
  84. *
  85. * @param string $db Database name
  86. * @param string $table Table name
  87. * @param string $searchType Whether normal or zoom search
  88. */
  89. public function __construct($db, $table, $searchType)
  90. {
  91. $this->_db = $db;
  92. $this->_table = $table;
  93. $this->_searchType = $searchType;
  94. $this->_columnNames = array();
  95. $this->_columnNullFlags = array();
  96. $this->_columnTypes = array();
  97. $this->_columnCollations = array();
  98. $this->_geomColumnFlag = false;
  99. $this->_foreigners = array();
  100. // Loads table's information
  101. $this->_loadTableInfo();
  102. }
  103. /**
  104. * Returns Column names array
  105. *
  106. * @return array column names
  107. */
  108. public function getColumnNames()
  109. {
  110. return $this->_columnNames;
  111. }
  112. /**
  113. * Gets all the columns of a table along with their types, collations
  114. * and whether null or not.
  115. *
  116. * @return void
  117. */
  118. private function _loadTableInfo()
  119. {
  120. // Gets the list and number of columns
  121. $columns = PMA_DBI_get_columns($this->_db, $this->_table, null, true);
  122. // Get details about the geometry fucntions
  123. $geom_types = PMA_Util::getGISDatatypes();
  124. foreach ($columns as $row) {
  125. // set column name
  126. $this->_columnNames[] = $row['Field'];
  127. $type = $row['Type'];
  128. // check whether table contains geometric columns
  129. if (in_array($type, $geom_types)) {
  130. $this->_geomColumnFlag = true;
  131. }
  132. // reformat mysql query output
  133. if (strncasecmp($type, 'set', 3) == 0
  134. || strncasecmp($type, 'enum', 4) == 0
  135. ) {
  136. $type = str_replace(',', ', ', $type);
  137. } else {
  138. // strip the "BINARY" attribute, except if we find "BINARY(" because
  139. // this would be a BINARY or VARBINARY column type
  140. if (! preg_match('@BINARY[\(]@i', $type)) {
  141. $type = preg_replace('@BINARY@i', '', $type);
  142. }
  143. $type = preg_replace('@ZEROFILL@i', '', $type);
  144. $type = preg_replace('@UNSIGNED@i', '', $type);
  145. $type = strtolower($type);
  146. }
  147. if (empty($type)) {
  148. $type = '&nbsp;';
  149. }
  150. $this->_columnTypes[] = $type;
  151. $this->_columnNullFlags[] = $row['Null'];
  152. $this->_columnCollations[]
  153. = ! empty($row['Collation']) && $row['Collation'] != 'NULL'
  154. ? $row['Collation']
  155. : '';
  156. } // end for
  157. // Retrieve foreign keys
  158. $this->_foreigners = PMA_getForeigners($this->_db, $this->_table);
  159. }
  160. /**
  161. * Sets the table header for displaying a table in query-by-example format.
  162. *
  163. * @return HTML content, the tags and content for table header
  164. */
  165. private function _getTableHeader()
  166. {
  167. // Display the Function column only if there is at least one geometry column
  168. $func = '';
  169. if ($this->_geomColumnFlag) {
  170. $func = '<th>' . __('Function') . '</th>';
  171. }
  172. return '<thead>
  173. <tr>' . $func . '<th>' . __('Column') . '</th>
  174. <th>' . __('Type') . '</th>
  175. <th>' . __('Collation') . '</th>
  176. <th>' . __('Operator') . '</th>
  177. <th>' . __('Value') . '</th>
  178. </tr>
  179. </thead>';
  180. }
  181. /**
  182. * Returns an array with necessary configrations to create
  183. * sub-tabs(Table Search and Zoom Search) in the table_select page.
  184. *
  185. * @return array Array containing configuration (icon, text, link, id, args)
  186. * of sub-tabs for Table Search and Zoom search
  187. */
  188. private function _getSubTabs()
  189. {
  190. $subtabs = array();
  191. $subtabs['search']['icon'] = 'b_search.png';
  192. $subtabs['search']['text'] = __('Table Search');
  193. $subtabs['search']['link'] = 'tbl_select.php';
  194. $subtabs['search']['id'] = 'tbl_search_id';
  195. $subtabs['search']['args']['pos'] = 0;
  196. $subtabs['zoom']['icon'] = 'b_props.png';
  197. $subtabs['zoom']['link'] = 'tbl_zoom_select.php';
  198. $subtabs['zoom']['text'] = __('Zoom Search');
  199. $subtabs['zoom']['id'] = 'zoom_search_id';
  200. return $subtabs;
  201. }
  202. /**
  203. * Provides html elements for search criteria inputbox
  204. * in case the column's type is geometrical
  205. *
  206. * @param int $column_index Column's index
  207. * @param bool $in_fbs Whether we are in 'function based search'
  208. *
  209. * @return HTML elements.
  210. */
  211. private function _getGeometricalInputBox($column_index, $in_fbs)
  212. {
  213. $html_output = '<input type="text" name="criteriaValues[' . $column_index . ']"'
  214. . ' size="40" class="textfield" id="field_' . $column_index . '" />';
  215. if ($in_fbs) {
  216. $edit_url = 'gis_data_editor.php?' . PMA_generate_common_url();
  217. $edit_str = PMA_Util::getIcon('b_edit.png', __('Edit/Insert'));
  218. $html_output .= '<span class="open_search_gis_editor">';
  219. $html_output .= PMA_Util::linkOrButton(
  220. $edit_url, $edit_str, array(), false, false, '_blank'
  221. );
  222. $html_output .= '</span>';
  223. }
  224. return $html_output;
  225. }
  226. /**
  227. * Provides html elements for search criteria inputbox
  228. * in case the column is a Foreign Key
  229. *
  230. * @param array $foreignData Foreign keys data
  231. * @param string $column_name Column name
  232. * @param int $column_index Column index
  233. * @param array $titles Selected title
  234. * @param int $foreignMaxLimit Max limit of displaying foreign elements
  235. * @param array $criteriaValues Array of search criteria inputs
  236. * @param string $column_id Column's inputbox's id
  237. * @param bool $in_zoom_search_edit Whether we are in zoom search edit
  238. *
  239. * @return HTML elements.
  240. */
  241. private function _getForeignKeyInputBox($foreignData, $column_name,
  242. $column_index, $titles, $foreignMaxLimit, $criteriaValues, $column_id,
  243. $in_zoom_search_edit = false
  244. ) {
  245. $html_output = '';
  246. if (is_array($foreignData['disp_row'])) {
  247. $html_output .= '<select name="criteriaValues[' . $column_index . ']"'
  248. . ' id="' . $column_id . $column_index .'">';
  249. $html_output .= PMA_foreignDropdown(
  250. $foreignData['disp_row'], $foreignData['foreign_field'],
  251. $foreignData['foreign_display'], '', $foreignMaxLimit
  252. );
  253. $html_output .= '</select>';
  254. } elseif ($foreignData['foreign_link'] == true) {
  255. $html_output .= '<input type="text" id="' . $column_id . $column_index . '"'
  256. . ' name="criteriaValues[' . $column_index . ']" id="field_'
  257. . md5($column_name) . '[' . $column_index .']" class="textfield"'
  258. . (isset($criteriaValues[$column_index])
  259. && is_string($criteriaValues[$column_index])
  260. ? (' value="' . $criteriaValues[$column_index] . '"')
  261. : '')
  262. . ' />';
  263. $html_output .= <<<EOT
  264. <a target="_blank" onclick="window.open(this.href, 'foreigners', 'width=640,height=240,scrollbars=yes'); return false" href="browse_foreigners.php?
  265. EOT;
  266. $html_output .= '' . PMA_generate_common_url($this->_db, $this->_table)
  267. . '&amp;field=' . urlencode($column_name) . '&amp;fieldkey='
  268. . $column_index . '&amp;fromsearch=1"';
  269. if ($in_zoom_search_edit) {
  270. $html_output .= ' class="browse_foreign"';
  271. }
  272. $html_output .= '>' . str_replace("'", "\'", $titles['Browse']) . '</a>';
  273. }
  274. return $html_output;
  275. }
  276. /**
  277. * Provides html elements for search criteria inputbox
  278. * in case the column is of ENUM or SET type
  279. *
  280. * @param int $column_index Column index
  281. * @param array $criteriaValues Array of search criteria inputs
  282. * @param string $column_type Column type
  283. * @param string $column_id Column's inputbox's id
  284. * @param bool $in_zoom_search_edit Whether we are in zoom search edit
  285. *
  286. * @return HTML elements.
  287. */
  288. private function _getEnumSetInputBox($column_index, $criteriaValues,
  289. $column_type, $column_id, $in_zoom_search_edit = false
  290. ) {
  291. $column_type = htmlspecialchars($column_type);
  292. $html_output = '';
  293. $value = explode(
  294. ', ',
  295. str_replace("'", '', substr($column_type, 5, -1))
  296. );
  297. $cnt_value = count($value);
  298. /*
  299. * Enum in edit mode --> dropdown
  300. * Enum in search mode --> multiselect
  301. * Set in edit mode --> multiselect
  302. * Set in search mode --> input (skipped here, so the 'else'
  303. * section would handle it)
  304. */
  305. if ((strncasecmp($column_type, 'enum', 4) && ! $in_zoom_search_edit)
  306. || (strncasecmp($column_type, 'set', 3) && $in_zoom_search_edit)
  307. ) {
  308. $html_output .= '<select name="criteriaValues[' . ($column_index)
  309. . ']" id="' . $column_id . $column_index .'">';
  310. } else {
  311. $html_output .= '<select name="criteriaValues[' . $column_index . ']"'
  312. . ' id="' . $column_id . $column_index . '" multiple="multiple"'
  313. . ' size="' . min(3, $cnt_value) . '">';
  314. }
  315. //Add select options
  316. for ($j = 0; $j < $cnt_value; $j++) {
  317. if (isset($criteriaValues[$column_index])
  318. && is_array($criteriaValues[$column_index])
  319. && in_array($value[$j], $criteriaValues[$column_index])
  320. ) {
  321. $html_output .= '<option value="' . $value[$j] . '" Selected>'
  322. . $value[$j] . '</option>';
  323. } else {
  324. $html_output .= '<option value="' . $value[$j] . '">'
  325. . $value[$j] . '</option>';
  326. }
  327. } // end for
  328. $html_output .= '</select>';
  329. return $html_output;
  330. }
  331. /**
  332. * Creates the HTML content for:
  333. * 1) Browsing foreign data for a column.
  334. * 2) Creating elements for search criteria input on columns.
  335. *
  336. * @param array $foreignData Foreign keys data
  337. * @param string $column_name Column name
  338. * @param string $column_type Column type
  339. * @param int $column_index Column index
  340. * @param array $titles Selected title
  341. * @param int $foreignMaxLimit Max limit of displaying foreign elements
  342. * @param array $criteriaValues Array of search criteria inputs
  343. * @param bool $in_fbs Whether we are in 'function based search'
  344. * @param bool $in_zoom_search_edit Whether we are in zoom search edit
  345. *
  346. * @return string HTML content for viewing foreign data and elements
  347. * for search criteria input.
  348. */
  349. private function _getInputbox($foreignData, $column_name, $column_type,
  350. $column_index, $titles, $foreignMaxLimit, $criteriaValues, $in_fbs = false,
  351. $in_zoom_search_edit = false
  352. ) {
  353. $str = '';
  354. $column_type = (string)$column_type;
  355. $column_id = ($in_zoom_search_edit) ? 'edit_fieldID_' : 'fieldID_';
  356. // Get inputbox based on different column types
  357. // (Foreign key, geometrical, enum)
  358. if ($this->_foreigners && isset($this->_foreigners[$column_name])) {
  359. $str .= $this->_getForeignKeyInputBox(
  360. $foreignData, $column_name, $column_index, $titles,
  361. $foreignMaxLimit, $criteriaValues, $column_id
  362. );
  363. } elseif (in_array($column_type, PMA_Util::getGISDatatypes())) {
  364. $str .= $this->_getGeometricalInputBox($column_index, $in_fbs);
  365. } elseif (strncasecmp($column_type, 'enum', 4) == 0
  366. || (strncasecmp($column_type, 'set', 3) == 0 && $in_zoom_search_edit)
  367. ) {
  368. $str .= $this->_getEnumSetInputBox(
  369. $column_index, $criteriaValues, $column_type, $column_id,
  370. $in_zoom_search_edit = false
  371. );
  372. } else {
  373. // other cases
  374. $the_class = 'textfield';
  375. if ($column_type == 'date') {
  376. $the_class .= ' datefield';
  377. } elseif ($column_type == 'datetime'
  378. || substr($column_type, 0, 9) == 'timestamp'
  379. ) {
  380. $the_class .= ' datetimefield';
  381. } elseif (substr($column_type, 0, 3) == 'bit') {
  382. $the_class .= ' bit';
  383. }
  384. $str .= '<input type="text" name="criteriaValues[' . $column_index . ']"'
  385. .' size="40" class="' . $the_class . '" id="'
  386. . $column_id . $column_index . '"'
  387. . (isset($criteriaValues[$column_index])
  388. && is_string($criteriaValues[$column_index])
  389. ? (' value="' . $criteriaValues[$column_index] . '"')
  390. : '')
  391. . ' />';
  392. }
  393. return $str;
  394. }
  395. /**
  396. * Return the where clause in case column's type is ENUM.
  397. *
  398. * @param mixed $criteriaValues Search criteria input
  399. * @param string $func_type Search function/operator
  400. *
  401. * @return string part of where clause.
  402. */
  403. private function _getEnumWhereClause($criteriaValues, $func_type)
  404. {
  405. if (! is_array($criteriaValues)) {
  406. $criteriaValues = explode(',', $criteriaValues);
  407. }
  408. $enum_selected_count = count($criteriaValues);
  409. if ($func_type == '=' && $enum_selected_count > 1) {
  410. $func_type = 'IN';
  411. $parens_open = '(';
  412. $parens_close = ')';
  413. } elseif ($func_type == '!=' && $enum_selected_count > 1) {
  414. $func_type = 'NOT IN';
  415. $parens_open = '(';
  416. $parens_close = ')';
  417. } else {
  418. $parens_open = '';
  419. $parens_close = '';
  420. }
  421. $enum_where = '\''
  422. . PMA_Util::sqlAddSlashes($criteriaValues[0]) . '\'';
  423. for ($e = 1; $e < $enum_selected_count; $e++) {
  424. $enum_where .= ', \''
  425. . PMA_Util::sqlAddSlashes($criteriaValues[$e]) . '\'';
  426. }
  427. return ' ' . $func_type . ' ' . $parens_open
  428. . $enum_where . $parens_close;
  429. }
  430. /**
  431. * Return the where clause for a geometrical column.
  432. *
  433. * @param mixed $criteriaValues Search criteria input
  434. * @param string $names Name of the column on which search is submitted
  435. * @param string $func_type Search function/operator
  436. * @param string $types Type of the field
  437. * @param bool $geom_func Whether geometry functions should be applied
  438. *
  439. * @return string part of where clause.
  440. */
  441. private function _getGeomWhereClause($criteriaValues, $names,
  442. $func_type, $types, $geom_func = null
  443. ) {
  444. $geom_unary_functions = array(
  445. 'IsEmpty' => 1,
  446. 'IsSimple' => 1,
  447. 'IsRing' => 1,
  448. 'IsClosed' => 1,
  449. );
  450. $where = '';
  451. // Get details about the geometry functions
  452. $geom_funcs = PMA_Util::getGISFunctions($types, true, false);
  453. // New output type is the output type of the function being applied
  454. $types = $geom_funcs[$geom_func]['type'];
  455. // If the function takes a single parameter
  456. if ($geom_funcs[$geom_func]['params'] == 1) {
  457. $backquoted_name = $geom_func . '(' . PMA_Util::backquote($names) . ')';
  458. } else {
  459. // If the function takes two parameters
  460. // create gis data from the criteria input
  461. $gis_data = PMA_Util::createGISData($criteriaValues);
  462. $where = $geom_func . '(' . PMA_Util::backquote($names) . ',' . $gis_data . ')';
  463. return $where;
  464. }
  465. // If the where clause is something like 'IsEmpty(`spatial_col_name`)'
  466. if (isset($geom_unary_functions[$geom_func])
  467. && trim($criteriaValues) == ''
  468. ) {
  469. $where = $backquoted_name;
  470. } elseif (in_array($types, PMA_Util::getGISDatatypes())
  471. && ! empty($criteriaValues)
  472. ) {
  473. // create gis data from the criteria input
  474. $gis_data = PMA_Util::createGISData($criteriaValues);
  475. $where = $backquoted_name . ' ' . $func_type . ' ' . $gis_data;
  476. }
  477. return $where;
  478. }
  479. /**
  480. * Return the where clause for query generation based on the inputs provided.
  481. *
  482. * @param mixed $criteriaValues Search criteria input
  483. * @param string $names Name of the column on which search is submitted
  484. * @param string $types Type of the field
  485. * @param string $collations Field collation
  486. * @param string $func_type Search function/operator
  487. * @param bool $unaryFlag Whether operator unary or not
  488. * @param bool $geom_func Whether geometry functions should be applied
  489. *
  490. * @return string generated where clause.
  491. */
  492. private function _getWhereClause($criteriaValues, $names, $types, $collations,
  493. $func_type, $unaryFlag, $geom_func = null
  494. ) {
  495. // If geometry function is set
  496. if ($geom_func != null && trim($geom_func) != '') {
  497. return $this->_getGeomWhereClause(
  498. $criteriaValues, $names, $func_type, $types, $geom_func
  499. );
  500. }
  501. $backquoted_name = PMA_Util::backquote($names);
  502. $where = '';
  503. if ($unaryFlag) {
  504. $criteriaValues = '';
  505. $where = $backquoted_name . ' ' . $func_type;
  506. } elseif (strncasecmp($types, 'enum', 4) == 0 && ! empty($criteriaValues)) {
  507. $where = $backquoted_name;
  508. $where .= $this->_getEnumWhereClause($criteriaValues, $func_type);
  509. } elseif ($criteriaValues != '') {
  510. // For these types we quote the value. Even if it's another type
  511. // (like INT), for a LIKE we always quote the value. MySQL converts
  512. // strings to numbers and numbers to strings as necessary
  513. // during the comparison
  514. if (preg_match('@char|binary|blob|text|set|date|time|year@i', $types)
  515. || strpos(' ' . $func_type, 'LIKE')
  516. ) {
  517. $quot = '\'';
  518. } else {
  519. $quot = '';
  520. }
  521. // LIKE %...%
  522. if ($func_type == 'LIKE %...%') {
  523. $func_type = 'LIKE';
  524. $criteriaValues = '%' . $criteriaValues . '%';
  525. }
  526. if ($func_type == 'REGEXP ^...$') {
  527. $func_type = 'REGEXP';
  528. $criteriaValues = '^' . $criteriaValues . '$';
  529. }
  530. if ($func_type == 'IN (...)'
  531. || $func_type == 'NOT IN (...)'
  532. || $func_type == 'BETWEEN'
  533. || $func_type == 'NOT BETWEEN'
  534. ) {
  535. $func_type = str_replace(' (...)', '', $func_type);
  536. // quote values one by one
  537. $values = explode(',', $criteriaValues);
  538. foreach ($values as &$value) {
  539. $value = $quot . PMA_Util::sqlAddSlashes(trim($value))
  540. . $quot;
  541. }
  542. if ($func_type == 'BETWEEN' || $func_type == 'NOT BETWEEN') {
  543. $where = $backquoted_name . ' ' . $func_type . ' '
  544. . (isset($values[0]) ? $values[0] : '')
  545. . ' AND ' . (isset($values[1]) ? $values[1] : '');
  546. } else {
  547. $where = $backquoted_name . ' ' . $func_type
  548. . ' (' . implode(',', $values) . ')';
  549. }
  550. } else {
  551. if ($func_type == 'LIKE %...%' || $func_type == 'LIKE') {
  552. $where = $backquoted_name . ' ' . $func_type . ' ' . $quot
  553. . PMA_Util::sqlAddSlashes($criteriaValues, true) . $quot;
  554. } else {
  555. $where = $backquoted_name . ' ' . $func_type . ' ' . $quot
  556. . PMA_Util::sqlAddSlashes($criteriaValues) . $quot;
  557. }
  558. }
  559. } // end if
  560. return $where;
  561. }
  562. /**
  563. * Builds the sql search query from the post parameters
  564. *
  565. * @return string the generated SQL query
  566. */
  567. public function buildSqlQuery()
  568. {
  569. $sql_query = 'SELECT ';
  570. // If only distinct values are needed
  571. $is_distinct = (isset($_POST['distinct'])) ? 'true' : 'false';
  572. if ($is_distinct == 'true') {
  573. $sql_query .= 'DISTINCT ';
  574. }
  575. // if all column names were selected to display, we do a 'SELECT *'
  576. // (more efficient and this helps prevent a problem in IE
  577. // if one of the rows is edited and we come back to the Select results)
  578. if (isset($_POST['zoom_submit']) || ! empty($_POST['displayAllColumns'])) {
  579. $sql_query .= '* ';
  580. } else {
  581. $sql_query .= implode(
  582. ', ',
  583. PMA_Util::backquote($_POST['columnsToDisplay'])
  584. );
  585. } // end if
  586. $sql_query .= ' FROM '
  587. . PMA_Util::backquote($_POST['table']);
  588. $whereClause = $this->_generateWhereClause();
  589. $sql_query .= $whereClause;
  590. // if the search results are to be ordered
  591. if (isset($_POST['orderByColumn']) && $_POST['orderByColumn'] != '--nil--') {
  592. $sql_query .= ' ORDER BY '
  593. . PMA_Util::backquote($_POST['orderByColumn'])
  594. . ' ' . $_POST['order'];
  595. } // end if
  596. return $sql_query;
  597. }
  598. /**
  599. * Generates the where clause for the SQL search query to be executed
  600. *
  601. * @return string the generated where clause
  602. */
  603. private function _generateWhereClause()
  604. {
  605. if (isset($_POST['customWhereClause'])
  606. && trim($_POST['customWhereClause']) != ''
  607. ) {
  608. return ' WHERE ' . $_POST['customWhereClause'];
  609. }
  610. // If there are no search criteria set or no unary criteria operators, return
  611. if (! isset($_POST['criteriaValues'])
  612. && ! isset($_POST['criteriaColumnOperators'])
  613. ) {
  614. return '';
  615. }
  616. // else continue to form the where clause from column criteria values
  617. $fullWhereClause = $charsets = array();
  618. reset($_POST['criteriaColumnOperators']);
  619. while (list($column_index, $operator) = each($_POST['criteriaColumnOperators'])) {
  620. list($charsets[$column_index]) = explode(
  621. '_', $_POST['criteriaColumnCollations'][$column_index]
  622. );
  623. $unaryFlag = $GLOBALS['PMA_Types']->isUnaryOperator($operator);
  624. $tmp_geom_func = isset($geom_func[$column_index])
  625. ? $geom_func[$column_index] : null;
  626. $whereClause = $this->_getWhereClause(
  627. $_POST['criteriaValues'][$column_index],
  628. $_POST['criteriaColumnNames'][$column_index],
  629. $_POST['criteriaColumnTypes'][$column_index],
  630. $_POST['criteriaColumnCollations'][$column_index],
  631. $operator,
  632. $unaryFlag,
  633. $tmp_geom_func
  634. );
  635. if ($whereClause) {
  636. $fullWhereClause[] = $whereClause;
  637. }
  638. } // end while
  639. if ($fullWhereClause) {
  640. return ' WHERE ' . implode(' AND ', $fullWhereClause);
  641. }
  642. return '';
  643. }
  644. /**
  645. * Generates HTML for a geometrical function column to be displayed in table
  646. * search selection form
  647. *
  648. * @param integer $column_index index of current column in $columnTypes array
  649. *
  650. * @return string the generated HTML
  651. */
  652. private function _getGeomFuncHtml($column_index)
  653. {
  654. $html_output = '';
  655. // return if geometrical column is not present
  656. if (! $this->_geomColumnFlag) {
  657. return $html_output;
  658. }
  659. /**
  660. * Displays 'Function' column if it is present
  661. */
  662. $html_output .= '<td>';
  663. $geom_types = PMA_Util::getGISDatatypes();
  664. // if a geometry column is present
  665. if (in_array($this->_columnTypes[$column_index], $geom_types)) {
  666. $html_output .= '<select class="geom_func" name="geom_func['
  667. . $column_index . ']">';
  668. // get the relevant list of GIS functions
  669. $funcs = PMA_Util::getGISFunctions($this->_columnTypes[$column_index], true, true);
  670. /**
  671. * For each function in the list of functions,
  672. * add an option to select list
  673. */
  674. foreach ($funcs as $func_name => $func) {
  675. $name = isset($func['display']) ? $func['display'] : $func_name;
  676. $html_output .= '<option value="' . htmlspecialchars($name) . '">'
  677. . htmlspecialchars($name) . '</option>';
  678. }
  679. $html_output .= '</select>';
  680. } else {
  681. $html_output .= '&nbsp;';
  682. }
  683. $html_output .= '</td>';
  684. return $html_output;
  685. }
  686. /**
  687. * Generates formatted HTML for extra search options in table search form
  688. *
  689. * @return string the generated HTML
  690. */
  691. private function _getOptions()
  692. {
  693. $html_output = '';
  694. $html_output .= PMA_Util::getDivForSliderEffect(
  695. 'searchoptions', __('Options')
  696. );
  697. /**
  698. * Displays columns select list for selecting distinct columns in the search
  699. */
  700. $html_output .= '<fieldset id="fieldset_select_fields">'
  701. . '<legend>' . __('Select columns (at least one):') . '</legend>'
  702. . '<select name="columnsToDisplay[]"'
  703. . ' size="' . min(count($this->_columnNames), 10) . '"'
  704. . ' multiple="multiple">';
  705. // Displays the list of the fields
  706. foreach ($this->_columnNames as $each_field) {
  707. $html_output .= ' '
  708. . '<option value="' . htmlspecialchars($each_field) . '"'
  709. . ' selected="selected">' . htmlspecialchars($each_field)
  710. . '</option>' . "\n";
  711. } // end for
  712. $html_output .= '</select>'
  713. . '<input type="checkbox" name="distinct" value="DISTINCT" id="oDistinct" />'
  714. . '<label for="oDistinct">DISTINCT</label></fieldset>';
  715. /**
  716. * Displays input box for custom 'Where' clause to be used in the search
  717. */
  718. $html_output .= '<fieldset id="fieldset_search_conditions">'
  719. . '<legend>' . '<em>' . __('Or') . '</em> '
  720. . __('Add search conditions (body of the "where" clause):') . '</legend>';
  721. $html_output .= PMA_Util::showMySQLDocu(
  722. 'SQL-Syntax', 'Functions'
  723. );
  724. $html_output .= '<input type="text" name="customWhereClause"'
  725. . ' class="textfield" size="64" />';
  726. $html_output .= '</fieldset>';
  727. /**
  728. * Displays option of changing default number of rows displayed per page
  729. */
  730. $html_output .= '<fieldset id="fieldset_limit_rows">'
  731. . '<legend>' . __('Number of rows per page') . '</legend>'
  732. . '<input type="text" size="4" name="session_max_rows" '
  733. . 'value="' . $GLOBALS['cfg']['MaxRows'] . '" class="textfield" />'
  734. . '</fieldset>';
  735. /**
  736. * Displays option for ordering search results
  737. * by a column value (Asc or Desc)
  738. */
  739. $html_output .= '<fieldset id="fieldset_display_order">'
  740. . '<legend>' . __('Display order:') . '</legend>'
  741. . '<select name="orderByColumn"><option value="--nil--"></option>';
  742. foreach ($this->_columnNames as $each_field) {
  743. $html_output .= ' '
  744. . '<option value="' . htmlspecialchars($each_field) . '">'
  745. . htmlspecialchars($each_field) . '</option>' . "\n";
  746. } // end for
  747. $html_output .= '</select>';
  748. $choices = array(
  749. 'ASC' => __('Ascending'),
  750. 'DESC' => __('Descending')
  751. );
  752. $html_output .= PMA_Util::getRadioFields(
  753. 'order', $choices, 'ASC', false, true, "formelement"
  754. );
  755. unset($choices);
  756. $html_output .= '</fieldset><br style="clear: both;"/></div>';
  757. return $html_output;
  758. }
  759. /**
  760. * Other search criteria like data label
  761. * (for tbl_zoom_select.php)
  762. *
  763. * @param array $dataLabel Label for points in zoom plot
  764. *
  765. * @return string the generated html
  766. */
  767. private function _getOptionsZoom($dataLabel)
  768. {
  769. $html_output = '';
  770. $html_output .= '<table class="data">';
  771. //Select options for datalabel
  772. $html_output .= '<tr>';
  773. $html_output .= '<td><label for="dataLabel">'
  774. . __("Use this column to label each point") . '</label></td>';
  775. $html_output .= '<td><select name="dataLabel" id="dataLabel" >'
  776. . '<option value = "">' . __('None') . '</option>';
  777. for ($j = 0; $j < count($this->_columnNames); $j++) {
  778. if (isset($dataLabel)
  779. && $dataLabel == htmlspecialchars($this->_columnNames[$j])
  780. ) {
  781. $html_output .= '<option value="'
  782. . htmlspecialchars($this->_columnNames[$j]) . '" selected="selected">'
  783. . htmlspecialchars($this->_columnNames[$j]) . '</option>';
  784. } else {
  785. $html_output .= '<option value="'
  786. . htmlspecialchars($this->_columnNames[$j]) . '" >'
  787. . htmlspecialchars($this->_columnNames[$j]) . '</option>';
  788. }
  789. }
  790. $html_output .= '</select></td>';
  791. $html_output .= '</tr>';
  792. //Inputbox for changing default maximum rows to plot
  793. $html_output .= '<tr>';
  794. $html_output .= '<td><label for="maxRowPlotLimit">'
  795. . __("Maximum rows to plot") . '</label></td>';
  796. $html_output .= '<td>';
  797. $html_output .= '<input type="text" name="maxPlotLimit"'
  798. . ' id="maxRowPlotLimit"'
  799. . ' value="' . ((! empty($_POST['maxPlotLimit']))
  800. ? htmlspecialchars($_POST['maxPlotLimit'])
  801. : $GLOBALS['cfg']['maxRowPlotLimit'])
  802. . '" />';
  803. $html_output .= '</td></tr>';
  804. $html_output .= '</table>';
  805. return $html_output;
  806. }
  807. /**
  808. * Provides a column's type, collation, operators list, and crietria value
  809. * to display in table search form
  810. *
  811. * @param integer $search_index Row number in table search form
  812. * @param integer $column_index Column index in ColumnNames array
  813. *
  814. * @return array Array contaning column's properties
  815. */
  816. public function getColumnProperties($search_index, $column_index)
  817. {
  818. $selected_operator = (isset($_POST['criteriaColumnOperators'])
  819. ? $_POST['criteriaColumnOperators'][$search_index] : '');
  820. $entered_value = (isset($_POST['criteriaValues'])
  821. ? $_POST['criteriaValues'] : '');
  822. $titles['Browse'] = PMA_Util::getIcon('b_browse.png', __('Browse foreign values'));
  823. //Gets column's type and collation
  824. $type = $this->_columnTypes[$column_index];
  825. $collation = $this->_columnCollations[$column_index];
  826. //Gets column's comparison operators depending on column type
  827. $func = '<select name="criteriaColumnOperators[' . $search_index . ']">';
  828. $func .= $GLOBALS['PMA_Types']->getTypeOperatorsHtml(
  829. preg_replace('@\(.*@s', '', $this->_columnTypes[$column_index]),
  830. $this->_columnNullFlags[$column_index], $selected_operator
  831. );
  832. $func .= '</select>';
  833. //Gets link to browse foreign data(if any) and criteria inputbox
  834. $foreignData = PMA_getForeignData(
  835. $this->_foreigners, $this->_columnNames[$column_index], false, '', ''
  836. );
  837. $value = $this->_getInputbox(
  838. $foreignData, $this->_columnNames[$column_index], $type, $search_index,
  839. $titles, $GLOBALS['cfg']['ForeignKeyMaxLimit'], $entered_value
  840. );
  841. return array(
  842. 'type' => $type,
  843. 'collation' => $collation,
  844. 'func' => $func,
  845. 'value' => $value
  846. );
  847. }
  848. /**
  849. * Provides the search form's table row in case of Normal Search
  850. * (for tbl_select.php)
  851. *
  852. * @return string the generated table row
  853. */
  854. private function _getRowsNormal()
  855. {
  856. $odd_row = true;
  857. $html_output = '';
  858. // for every column present in table
  859. for ($column_index = 0; $column_index < count($this->_columnNames); $column_index++) {
  860. $html_output .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
  861. $odd_row = !$odd_row;
  862. //If 'Function' column is present
  863. $html_output .= $this->_getGeomFuncHtml($column_index);
  864. //Displays column's name, type, collation and value
  865. $html_output .= '<th>'
  866. . htmlspecialchars($this->_columnNames[$column_index]) . '</th>';
  867. $properties = $this->getColumnProperties($column_index, $column_index);
  868. $html_output .= '<td>'
  869. . htmlspecialchars($properties['type'])
  870. . '</td>';
  871. $html_output .= '<td>' . $properties['collation'] . '</td>';
  872. $html_output .= '<td>' . $properties['func'] . '</td>';
  873. $html_output .= '<td>' . $properties['value'] . '</td>';
  874. $html_output .= '</tr>';
  875. //Displays hidden fields
  876. $html_output .= '<tr><td>';
  877. $html_output .= '<input type="hidden"'
  878. . ' name="criteriaColumnNames[' . $column_index . ']"'
  879. . ' value="'
  880. . htmlspecialchars($this->_columnNames[$column_index])
  881. . '" />';
  882. $html_output .= '<input type="hidden"'
  883. . ' name="criteriaColumnTypes[' . $column_index . ']"'
  884. . ' value="'
  885. . htmlspecialchars($this->_columnTypes[$column_index]) . '" />';
  886. $html_output .= '<input type="hidden"'
  887. . ' name="criteriaColumnCollations[' . $column_index . ']"'
  888. . ' value="' . $this->_columnCollations[$column_index] . '" />';
  889. $html_output .= '</td></tr>';
  890. } // end for
  891. return $html_output;
  892. }
  893. /**
  894. * Provides the search form's table row in case of Zoom Search
  895. * (for tbl_zoom_select.php)
  896. *
  897. * @return string the generated table row
  898. */
  899. private function _getRowsZoom()
  900. {
  901. $odd_row = true;
  902. $html_output = '';
  903. /**
  904. * Get already set search criteria (if any)
  905. */
  906. //Displays column rows for search criteria input
  907. for ($i = 0; $i < 4; $i++) {
  908. //After X-Axis and Y-Axis column rows, display additional criteria option
  909. if ($i == 2) {
  910. $html_output .= '<tr><td>';
  911. $html_output .= __("Additional search criteria");
  912. $html_output .= '</td></tr>';
  913. }
  914. $html_output .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
  915. $odd_row = ! $odd_row;
  916. //Select options for column names
  917. $html_output .= '<th><select name="criteriaColumnNames[]" id="'
  918. . 'tableid_' . $i . '" >';
  919. $html_output .= '<option value="' . 'pma_null' . '">' . __('None')
  920. . '</option>';
  921. for ($j = 0 ; $j < count($this->_columnNames); $j++) {
  922. if (isset($_POST['criteriaColumnNames'][$i])
  923. && $_POST['criteriaColumnNames'][$i] == htmlspecialchars($this->_columnNames[$j])
  924. ) {
  925. $html_output .= '<option value="'
  926. . htmlspecialchars($this->_columnNames[$j]) . '" selected="selected">'
  927. . htmlspecialchars($this->_columnNames[$j]) . '</option>';
  928. } else {
  929. $html_output .= '<option value="'
  930. . htmlspecialchars($this->_columnNames[$j]) . '">'
  931. . htmlspecialchars($this->_columnNames[$j]) . '</option>';
  932. }
  933. }
  934. $html_output .= '</select></th>';
  935. if (isset($_POST['criteriaColumnNames'])
  936. && $_POST['criteriaColumnNames'][$i] != 'pma_null'
  937. ) {
  938. $key = array_search(
  939. $_POST['criteriaColumnNames'][$i],
  940. $this->_columnNames
  941. );
  942. $properties = $this->getColumnProperties($i, $key);
  943. $type[$i] = $properties['type'];
  944. $collation[$i] = $properties['collation'];
  945. $func[$i] = $properties['func'];
  946. $value[$i] = $properties['value'];
  947. }
  948. //Column type
  949. $html_output .= '<td>' . (isset($type[$i]) ? htmlspecialchars($type[$i]) : '') . '</td>';
  950. //Column Collation
  951. $html_output .= '<td>' . (isset($collation[$i]) ? $collation[$i] : '')
  952. . '</td>';
  953. //Select options for column operators
  954. $html_output .= '<td>' . (isset($func[$i]) ? $func[$i] : '') . '</td>';
  955. //Inputbox for search criteria value
  956. $html_output .= '<td>' . (isset($value[$i]) ? htmlspecialchars($value[$i]) : '') . '</td>';
  957. $html_output .= '</tr>';
  958. //Displays hidden fields
  959. $html_output .= '<tr><td>';
  960. $html_output .= '<input type="hidden" name="criteriaColumnTypes[' . $i . ']"'
  961. . ' id="types_' . $i . '" ';
  962. if (isset($_POST['criteriaColumnTypes'][$i])) {
  963. $html_output .= 'value="' . htmlspecialchars($_POST['criteriaColumnTypes'][$i]) . '" ';
  964. }
  965. $html_output .= '/>';
  966. $html_output .= '<input type="hidden" name="criteriaColumnCollations['
  967. . $i . ']" id="collations_' . $i . '" />';
  968. $html_output .= '</td></tr>';
  969. }//end for
  970. return $html_output;
  971. }
  972. /**
  973. * Generates HTML for displaying fields table in search form
  974. *
  975. * @return string the generated HTML
  976. */
  977. private function _getFieldsTableHtml()
  978. {
  979. $html_output = '';
  980. $html_output .= '<table class="data"'
  981. . ($this->_searchType == 'zoom' ? ' id="tableFieldsId"' : '') . '>';
  982. $html_output .= $this->_getTableHeader();
  983. $html_output .= '<tbody>';
  984. if ($this->_searchType == 'zoom') {
  985. $html_output .= $this->_getRowsZoom();
  986. } else {
  987. $html_output .= $this->_getRowsNormal();
  988. }
  989. $html_output .= '</tbody></table>';
  990. return $html_output;
  991. }
  992. /**
  993. * Provides the form tag for table search form
  994. * (normal search or zoom search)
  995. *
  996. * @param string $goto Goto URL
  997. *
  998. * @return string the HTML for form tag
  999. */
  1000. private function _getFormTag($goto)
  1001. {
  1002. $html_output = '';
  1003. $scriptName = ($this->_searchType == 'zoom' ? 'tbl_zoom_select.php' : 'tbl_select.php');
  1004. $formId = ($this->_searchType == 'zoom' ? 'zoom_search_form' : 'tbl_search_form');
  1005. $html_output .= '<form method="post" action="' . $scriptName . '" '
  1006. . 'name="insertForm" id="' . $formId . '" '
  1007. . 'class="ajax"' . '>';
  1008. $html_output .= PMA_generate_common_hidden_inputs($this->_db, $this->_table);
  1009. $html_output .= '<input type="hidden" name="goto" value="' . $goto . '" />';
  1010. $html_output .= '<input type="hidden" name="back" value="' . $scriptName
  1011. . '" />';
  1012. return $html_output;
  1013. }
  1014. /**
  1015. * Generates the table search form under table search tab
  1016. *
  1017. * @param string $goto Goto URL
  1018. * @param string $dataLabel Label for points in zoom plot
  1019. *
  1020. * @return string the generated HTML for table search form
  1021. */
  1022. public function getSelectionForm($goto, $dataLabel = null)
  1023. {
  1024. $url_params = array();
  1025. $url_params['db'] = $this->_db;
  1026. $url_params['table'] = $this->_table;
  1027. $html_output = '<ul id="topmenu2">';
  1028. foreach ($this->_getSubTabs() as $tab) {
  1029. $html_output .= PMA_Util::getHtmlTab($tab, $url_params);
  1030. }
  1031. $html_output .= '</ul>';
  1032. $html_output .= '<div class="clearfloat"></div>';
  1033. $html_output .= $this->_getFormTag($goto);
  1034. if ($this->_searchType == 'zoom') {
  1035. $html_output .= '<fieldset id="fieldset_zoom_search">';
  1036. $html_output .= '<fieldset id="inputSection">';
  1037. $html_output .= '<legend>'
  1038. . __('Do a "query by example" (wildcard: "%") for two different columns')
  1039. . '</legend>';
  1040. $html_output .= $this->_getFieldsTableHtml();
  1041. $html_output .= $this->_getOptionsZoom($dataLabel);
  1042. $html_output .= '</fieldset>';
  1043. $html_output .= '</fieldset>';
  1044. } else {
  1045. $html_output .= '<fieldset id="fieldset_table_search">';
  1046. $html_output .= '<fieldset id="fieldset_table_qbe">';
  1047. $html_output .= '<legend>'
  1048. . __('Do a "query by example" (wildcard: "%")')
  1049. . '</legend>';
  1050. $html_output .= $this->_getFieldsTableHtml();
  1051. $html_output .= '<div id="gis_editor"></div>';
  1052. $html_output .= '<div id="popup_background"></div>';
  1053. $html_output .= '</fieldset>';
  1054. $html_output .= $this->_getOptions();
  1055. $html_output .= '</fieldset>';
  1056. }
  1057. /**
  1058. * Displays selection form's footer elements
  1059. */
  1060. $html_output .= '<fieldset class="tblFooters">';
  1061. $html_output .= '<input type="submit" name="'
  1062. . ($this->_searchType == 'zoom' ? 'zoom_submit' : 'submit')
  1063. . ($this->_searchType == 'zoom' ? '" id="inputFormSubmitId"' : '" ')
  1064. . 'value="' . __('Go') . '" />';
  1065. $html_output .= '</fieldset></form>';
  1066. $html_output .= '<div id="sqlqueryresults"></div>';
  1067. return $html_output;
  1068. }
  1069. /**
  1070. * Provides form for displaying point data and also the scatter plot
  1071. * (for tbl_zoom_select.php)
  1072. *
  1073. * @param string $goto Goto URL
  1074. * @param array $data Array containing SQL query data
  1075. *
  1076. * @return string form's html
  1077. */
  1078. public function getZoomResultsForm($goto, $data)
  1079. {
  1080. $html_output = '';
  1081. $titles['Browse'] = PMA_Util::getIcon('b_browse.png', __('Browse foreign values'));
  1082. $html_output .= '<form method="post" action="tbl_zoom_select.php"'
  1083. . ' name="displayResultForm" id="zoom_display_form"'
  1084. . ' class="ajax"' . '>';
  1085. $html_output .= PMA_generate_common_hidden_inputs($this->_db, $this->_table);
  1086. $html_output .= '<input type="hidden" name="goto" value="' . $goto . '" />';
  1087. $html_output .= '<input type="hidden" name="back" value="tbl_zoom_select.php" />';
  1088. $html_output .= '<fieldset id="displaySection">';
  1089. $html_output .= '<legend>' . __('Browse/Edit the points') . '</legend>';
  1090. //JSON encode the data(query result)
  1091. $html_output .= '<center>';
  1092. if (isset($_POST['zoom_submit']) && ! empty($data)) {
  1093. $html_output .= '<div id="resizer">';
  1094. $html_output .= '<center><a href="#" onclick="displayHelp();">'
  1095. . __('How to use') . '</a></center>';
  1096. $html_output .= '<div id="querydata" style="display:none">'
  1097. . htmlspecialchars(json_encode($data)) . '</div>';
  1098. $html_output .= '<div id="querychart"></div>';
  1099. $html_output .= '<button class="button-reset">'
  1100. . __('Reset zoom') . '</button>';
  1101. $html_output .= '</div>';
  1102. }
  1103. $html_output .= '</center>';
  1104. //Displays rows in point edit form
  1105. $html_output .= '<div id="dataDisplay" style="display:none">';
  1106. $html_output .= '<table><thead>';
  1107. $html_output .= '<tr>';
  1108. $html_output .= '<th>' . __('Column') . '</th>'
  1109. . '<th>' . __('Null') . '</th>'
  1110. . '<th>' . __('Value') . '</th>';
  1111. $html_output .= '</tr>';
  1112. $html_output .= '</thead>';
  1113. $html_output .= '<tbody>';
  1114. $odd_row = true;
  1115. for ($column_index = 0; $column_index < count($this->_columnNames); $column_index++) {
  1116. $fieldpopup = $this->_columnNames[$column_index];
  1117. $foreignData = PMA_getForeignData($this->_foreigners, $fieldpopup, false, '', '');
  1118. $html_output .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
  1119. $odd_row = ! $odd_row;
  1120. //Display column Names
  1121. $html_output .= '<th>' . htmlspecialchars($this->_columnNames[$column_index])
  1122. . '</th>';
  1123. //Null checkbox if column can be null
  1124. $html_output .= '<th>' . (($this->_columnNullFlags[$column_index] == 'YES')
  1125. ? '<input type="checkbox" class="checkbox_null"'
  1126. . ' name="criteriaColumnNullFlags[' . $column_index . ']"'
  1127. . ' id="edit_fields_null_id_' . $column_index . '" />'
  1128. : '');
  1129. $html_output .= '</th>';
  1130. //Column's Input box
  1131. $html_output .= '<th>';
  1132. $html_output .= $this->_getInputbox(
  1133. $foreignData, $fieldpopup, $this->_columnTypes[$column_index],
  1134. $column_index, $titles, $GLOBALS['cfg']['ForeignKeyMaxLimit'],
  1135. '', false, true
  1136. );
  1137. $html_output .= '</th></tr>';
  1138. }
  1139. $html_output .= '</tbody></table>';
  1140. $html_output .= '</div>';
  1141. $html_output .= '<input type="hidden" id="queryID" name="sql_query" />';
  1142. $html_output .= '</form>';
  1143. return $html_output;
  1144. }
  1145. }
  1146. ?>