TableSearch.class.php 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478
  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 = $GLOBALS['dbi']->getColumns(
  122. $this->_db, $this->_table, null, true
  123. );
  124. // Get details about the geometry fucntions
  125. $geom_types = PMA_Util::getGISDatatypes();
  126. foreach ($columns as $row) {
  127. // set column name
  128. $this->_columnNames[] = $row['Field'];
  129. $type = $row['Type'];
  130. // check whether table contains geometric columns
  131. if (in_array($type, $geom_types)) {
  132. $this->_geomColumnFlag = true;
  133. }
  134. // reformat mysql query output
  135. if (strncasecmp($type, 'set', 3) == 0
  136. || strncasecmp($type, 'enum', 4) == 0
  137. ) {
  138. $type = str_replace(',', ', ', $type);
  139. } else {
  140. // strip the "BINARY" attribute, except if we find "BINARY(" because
  141. // this would be a BINARY or VARBINARY column type
  142. if (! preg_match('@BINARY[\(]@i', $type)) {
  143. $type = preg_replace('@BINARY@i', '', $type);
  144. }
  145. $type = preg_replace('@ZEROFILL@i', '', $type);
  146. $type = preg_replace('@UNSIGNED@i', '', $type);
  147. $type = strtolower($type);
  148. }
  149. if (empty($type)) {
  150. $type = '&nbsp;';
  151. }
  152. $this->_columnTypes[] = $type;
  153. $this->_columnNullFlags[] = $row['Null'];
  154. $this->_columnCollations[]
  155. = ! empty($row['Collation']) && $row['Collation'] != 'NULL'
  156. ? $row['Collation']
  157. : '';
  158. } // end for
  159. // Retrieve foreign keys
  160. $this->_foreigners = PMA_getForeigners($this->_db, $this->_table);
  161. }
  162. /**
  163. * Sets the table header for displaying a table in query-by-example format.
  164. *
  165. * @return string HTML content, the tags and content for table header
  166. */
  167. private function _getTableHeader()
  168. {
  169. // Display the Function column only if there is at least one geometry column
  170. $func = '';
  171. if ($this->_geomColumnFlag) {
  172. $func = '<th>' . __('Function') . '</th>';
  173. }
  174. return '<thead>
  175. <tr>' . $func . '<th>' . __('Column') . '</th>
  176. <th>' . __('Type') . '</th>
  177. <th>' . __('Collation') . '</th>
  178. <th>' . __('Operator') . '</th>
  179. <th>' . __('Value') . '</th>
  180. </tr>
  181. </thead>';
  182. }
  183. /**
  184. * Returns an array with necessary configurations to create
  185. * sub-tabs in the table_select page.
  186. *
  187. * @return array Array containing configuration (icon, text, link, id, args)
  188. * of sub-tabs
  189. */
  190. private function _getSubTabs()
  191. {
  192. $subtabs = array();
  193. $subtabs['search']['icon'] = 'b_search.png';
  194. $subtabs['search']['text'] = __('Table Search');
  195. $subtabs['search']['link'] = 'tbl_select.php';
  196. $subtabs['search']['id'] = 'tbl_search_id';
  197. $subtabs['search']['args']['pos'] = 0;
  198. $subtabs['zoom']['icon'] = 'b_props.png';
  199. $subtabs['zoom']['link'] = 'tbl_zoom_select.php';
  200. $subtabs['zoom']['text'] = __('Zoom Search');
  201. $subtabs['zoom']['id'] = 'zoom_search_id';
  202. $subtabs['replace']['icon'] = 'b_find_replace.png';
  203. $subtabs['replace']['link'] = 'tbl_find_replace.php';
  204. $subtabs['replace']['text'] = __('Find and Replace');
  205. $subtabs['replace']['id'] = 'find_replace_id';
  206. return $subtabs;
  207. }
  208. /**
  209. * Provides html elements for search criteria inputbox
  210. * in case the column's type is geometrical
  211. *
  212. * @param int $column_index Column's index
  213. * @param bool $in_fbs Whether we are in 'function based search'
  214. *
  215. * @return string HTML elements.
  216. */
  217. private function _getGeometricalInputBox($column_index, $in_fbs)
  218. {
  219. $html_output = '<input type="text" name="criteriaValues['
  220. . $column_index . ']"'
  221. . ' size="40" class="textfield" id="field_' . $column_index . '" />';
  222. if ($in_fbs) {
  223. $edit_url = 'gis_data_editor.php?' . PMA_URL_getCommon();
  224. $edit_str = PMA_Util::getIcon('b_edit.png', __('Edit/Insert'));
  225. $html_output .= '<span class="open_search_gis_editor">';
  226. $html_output .= PMA_Util::linkOrButton(
  227. $edit_url, $edit_str, array(), false, false, '_blank'
  228. );
  229. $html_output .= '</span>';
  230. }
  231. return $html_output;
  232. }
  233. /**
  234. * Provides html elements for search criteria inputbox
  235. * in case the column is a Foreign Key
  236. *
  237. * @param array $foreignData Foreign keys data
  238. * @param string $column_name Column name
  239. * @param int $column_index Column index
  240. * @param array $titles Selected title
  241. * @param int $foreignMaxLimit Max limit of displaying foreign elements
  242. * @param array $criteriaValues Array of search criteria inputs
  243. * @param string $column_id Column's inputbox's id
  244. * @param bool $in_zoom_search_edit Whether we are in zoom search edit
  245. *
  246. * @return string HTML elements.
  247. */
  248. private function _getForeignKeyInputBox($foreignData, $column_name,
  249. $column_index, $titles, $foreignMaxLimit, $criteriaValues, $column_id,
  250. $in_zoom_search_edit = false
  251. ) {
  252. $html_output = '';
  253. if (is_array($foreignData['disp_row'])) {
  254. $html_output .= '<select name="criteriaValues[' . $column_index . ']"'
  255. . ' id="' . $column_id . $column_index . '">';
  256. $html_output .= PMA_foreignDropdown(
  257. $foreignData['disp_row'], $foreignData['foreign_field'],
  258. $foreignData['foreign_display'], '', $foreignMaxLimit
  259. );
  260. $html_output .= '</select>';
  261. } elseif ($foreignData['foreign_link'] == true) {
  262. $html_output .= '<input type="text" id="' . $column_id
  263. . $column_index . '"'
  264. . ' name="criteriaValues[' . $column_index . ']" id="field_'
  265. . md5($column_name) . '[' . $column_index . ']" class="textfield"'
  266. . (isset($criteriaValues[$column_index])
  267. && is_string($criteriaValues[$column_index])
  268. ? (' value="' . $criteriaValues[$column_index] . '"')
  269. : '')
  270. . ' />';
  271. $html_output .= <<<EOT
  272. <a target="_blank" onclick="window.open(this.href, 'foreigners', 'width=640,height=240,scrollbars=yes'); return false" href="browse_foreigners.php?
  273. EOT;
  274. $html_output .= '' . PMA_URL_getCommon($this->_db, $this->_table)
  275. . '&amp;field=' . urlencode($column_name) . '&amp;fieldkey='
  276. . $column_index . '&amp;fromsearch=1"';
  277. if ($in_zoom_search_edit) {
  278. $html_output .= ' class="browse_foreign"';
  279. }
  280. $html_output .= '>' . str_replace("'", "\'", $titles['Browse']) . '</a>';
  281. }
  282. return $html_output;
  283. }
  284. /**
  285. * Provides html elements for search criteria inputbox
  286. * in case the column is of ENUM or SET type
  287. *
  288. * @param int $column_index Column index
  289. * @param array $criteriaValues Array of search criteria inputs
  290. * @param string $column_type Column type
  291. * @param string $column_id Column's inputbox's id
  292. * @param bool $in_zoom_search_edit Whether we are in zoom search edit
  293. *
  294. * @return string HTML elements.
  295. */
  296. private function _getEnumSetInputBox($column_index, $criteriaValues,
  297. $column_type, $column_id, $in_zoom_search_edit = false
  298. ) {
  299. $html_output = '';
  300. $value = explode(
  301. ', ',
  302. str_replace("'", '', substr($column_type, 5, -1))
  303. );
  304. $cnt_value = count($value);
  305. /*
  306. * Enum in edit mode --> dropdown
  307. * Enum in search mode --> multiselect
  308. * Set in edit mode --> multiselect
  309. * Set in search mode --> input (skipped here, so the 'else'
  310. * section would handle it)
  311. */
  312. if ((strncasecmp($column_type, 'enum', 4) && ! $in_zoom_search_edit)
  313. || (strncasecmp($column_type, 'set', 3) && $in_zoom_search_edit)
  314. ) {
  315. $html_output .= '<select name="criteriaValues[' . ($column_index)
  316. . ']" id="' . $column_id . $column_index . '">';
  317. } else {
  318. $html_output .= '<select name="criteriaValues[' . $column_index . ']"'
  319. . ' id="' . $column_id . $column_index . '" multiple="multiple"'
  320. . ' size="' . min(3, $cnt_value) . '">';
  321. }
  322. //Add select options
  323. for ($j = 0; $j < $cnt_value; $j++) {
  324. if (isset($criteriaValues[$column_index])
  325. && is_array($criteriaValues[$column_index])
  326. && in_array($value[$j], $criteriaValues[$column_index])
  327. ) {
  328. $html_output .= '<option value="' . $value[$j] . '" Selected>'
  329. . $value[$j] . '</option>';
  330. } else {
  331. $html_output .= '<option value="' . $value[$j] . '">'
  332. . $value[$j] . '</option>';
  333. }
  334. } // end for
  335. $html_output .= '</select>';
  336. return $html_output;
  337. }
  338. /**
  339. * Creates the HTML content for:
  340. * 1) Browsing foreign data for a column.
  341. * 2) Creating elements for search criteria input on columns.
  342. *
  343. * @param array $foreignData Foreign keys data
  344. * @param string $column_name Column name
  345. * @param string $column_type Column type
  346. * @param int $column_index Column index
  347. * @param array $titles Selected title
  348. * @param int $foreignMaxLimit Max limit of displaying foreign elements
  349. * @param array $criteriaValues Array of search criteria inputs
  350. * @param bool $in_fbs Whether we are in 'function based search'
  351. * @param bool $in_zoom_search_edit Whether we are in zoom search edit
  352. *
  353. * @return string HTML content for viewing foreign data and elements
  354. * for search criteria input.
  355. */
  356. private function _getInputbox($foreignData, $column_name, $column_type,
  357. $column_index, $titles, $foreignMaxLimit, $criteriaValues, $in_fbs = false,
  358. $in_zoom_search_edit = false
  359. ) {
  360. $str = '';
  361. $column_type = (string)$column_type;
  362. $column_id = ($in_zoom_search_edit) ? 'edit_fieldID_' : 'fieldID_';
  363. // Get inputbox based on different column types
  364. // (Foreign key, geometrical, enum)
  365. if ($this->_foreigners && isset($this->_foreigners[$column_name])) {
  366. $str .= $this->_getForeignKeyInputBox(
  367. $foreignData, $column_name, $column_index, $titles,
  368. $foreignMaxLimit, $criteriaValues, $column_id
  369. );
  370. } elseif (in_array($column_type, PMA_Util::getGISDatatypes())) {
  371. $str .= $this->_getGeometricalInputBox($column_index, $in_fbs);
  372. } elseif (strncasecmp($column_type, 'enum', 4) == 0
  373. || (strncasecmp($column_type, 'set', 3) == 0 && $in_zoom_search_edit)
  374. ) {
  375. $str .= $this->_getEnumSetInputBox(
  376. $column_index, $criteriaValues, $column_type, $column_id,
  377. $in_zoom_search_edit = false
  378. );
  379. } else {
  380. // other cases
  381. $the_class = 'textfield';
  382. if ($column_type == 'date') {
  383. $the_class .= ' datefield';
  384. } elseif ($column_type == 'datetime'
  385. || substr($column_type, 0, 9) == 'timestamp'
  386. ) {
  387. $the_class .= ' datetimefield';
  388. } elseif (substr($column_type, 0, 3) == 'bit') {
  389. $the_class .= ' bit';
  390. }
  391. $str .= '<input type="text" name="criteriaValues[' . $column_index . ']"'
  392. . ' size="40" class="' . $the_class . '" id="'
  393. . $column_id . $column_index . '"'
  394. . (isset($criteriaValues[$column_index])
  395. && is_string($criteriaValues[$column_index])
  396. ? (' value="' . $criteriaValues[$column_index] . '"')
  397. : '')
  398. . ' />';
  399. }
  400. return $str;
  401. }
  402. /**
  403. * Return the where clause in case column's type is ENUM.
  404. *
  405. * @param mixed $criteriaValues Search criteria input
  406. * @param string $func_type Search function/operator
  407. *
  408. * @return string part of where clause.
  409. */
  410. private function _getEnumWhereClause($criteriaValues, $func_type)
  411. {
  412. if (! is_array($criteriaValues)) {
  413. $criteriaValues = explode(',', $criteriaValues);
  414. }
  415. $enum_selected_count = count($criteriaValues);
  416. if ($func_type == '=' && $enum_selected_count > 1) {
  417. $func_type = 'IN';
  418. $parens_open = '(';
  419. $parens_close = ')';
  420. } elseif ($func_type == '!=' && $enum_selected_count > 1) {
  421. $func_type = 'NOT IN';
  422. $parens_open = '(';
  423. $parens_close = ')';
  424. } else {
  425. $parens_open = '';
  426. $parens_close = '';
  427. }
  428. $enum_where = '\''
  429. . PMA_Util::sqlAddSlashes($criteriaValues[0]) . '\'';
  430. for ($e = 1; $e < $enum_selected_count; $e++) {
  431. $enum_where .= ', \''
  432. . PMA_Util::sqlAddSlashes($criteriaValues[$e]) . '\'';
  433. }
  434. return ' ' . $func_type . ' ' . $parens_open
  435. . $enum_where . $parens_close;
  436. }
  437. /**
  438. * Return the where clause for a geometrical column.
  439. *
  440. * @param mixed $criteriaValues Search criteria input
  441. * @param string $names Name of the column on which search is submitted
  442. * @param string $func_type Search function/operator
  443. * @param string $types Type of the field
  444. * @param bool $geom_func Whether geometry functions should be applied
  445. *
  446. * @return string part of where clause.
  447. */
  448. private function _getGeomWhereClause($criteriaValues, $names,
  449. $func_type, $types, $geom_func = null
  450. ) {
  451. $geom_unary_functions = array(
  452. 'IsEmpty' => 1,
  453. 'IsSimple' => 1,
  454. 'IsRing' => 1,
  455. 'IsClosed' => 1,
  456. );
  457. $where = '';
  458. // Get details about the geometry functions
  459. $geom_funcs = PMA_Util::getGISFunctions($types, true, false);
  460. // New output type is the output type of the function being applied
  461. $types = $geom_funcs[$geom_func]['type'];
  462. // If the function takes a single parameter
  463. if ($geom_funcs[$geom_func]['params'] == 1) {
  464. $backquoted_name = $geom_func . '(' . PMA_Util::backquote($names) . ')';
  465. } else {
  466. // If the function takes two parameters
  467. // create gis data from the criteria input
  468. $gis_data = PMA_Util::createGISData($criteriaValues);
  469. $where = $geom_func . '(' . PMA_Util::backquote($names)
  470. . ',' . $gis_data . ')';
  471. return $where;
  472. }
  473. // If the where clause is something like 'IsEmpty(`spatial_col_name`)'
  474. if (isset($geom_unary_functions[$geom_func])
  475. && trim($criteriaValues) == ''
  476. ) {
  477. $where = $backquoted_name;
  478. } elseif (in_array($types, PMA_Util::getGISDatatypes())
  479. && ! empty($criteriaValues)
  480. ) {
  481. // create gis data from the criteria input
  482. $gis_data = PMA_Util::createGISData($criteriaValues);
  483. $where = $backquoted_name . ' ' . $func_type . ' ' . $gis_data;
  484. }
  485. return $where;
  486. }
  487. /**
  488. * Return the where clause for query generation based on the inputs provided.
  489. *
  490. * @param mixed $criteriaValues Search criteria input
  491. * @param string $names Name of the column on which search is submitted
  492. * @param string $types Type of the field
  493. * @param string $collations Field collation
  494. * @param string $func_type Search function/operator
  495. * @param bool $unaryFlag Whether operator unary or not
  496. * @param bool $geom_func Whether geometry functions should be applied
  497. *
  498. * @return string generated where clause.
  499. */
  500. private function _getWhereClause($criteriaValues, $names, $types, $collations,
  501. $func_type, $unaryFlag, $geom_func = null
  502. ) {
  503. // If geometry function is set
  504. if ($geom_func != null && trim($geom_func) != '') {
  505. return $this->_getGeomWhereClause(
  506. $criteriaValues, $names, $func_type, $types, $geom_func
  507. );
  508. }
  509. $backquoted_name = PMA_Util::backquote($names);
  510. $where = '';
  511. if ($unaryFlag) {
  512. $where = $backquoted_name . ' ' . $func_type;
  513. } elseif (strncasecmp($types, 'enum', 4) == 0 && ! empty($criteriaValues)) {
  514. $where = $backquoted_name;
  515. $where .= $this->_getEnumWhereClause($criteriaValues, $func_type);
  516. } elseif ($criteriaValues != '') {
  517. // For these types we quote the value. Even if it's another type
  518. // (like INT), for a LIKE we always quote the value. MySQL converts
  519. // strings to numbers and numbers to strings as necessary
  520. // during the comparison
  521. if (preg_match('@char|binary|blob|text|set|date|time|year@i', $types)
  522. || strpos(' ' . $func_type, 'LIKE')
  523. ) {
  524. $quot = '\'';
  525. } else {
  526. $quot = '';
  527. }
  528. // LIKE %...%
  529. if ($func_type == 'LIKE %...%') {
  530. $func_type = 'LIKE';
  531. $criteriaValues = '%' . $criteriaValues . '%';
  532. }
  533. if ($func_type == 'REGEXP ^...$') {
  534. $func_type = 'REGEXP';
  535. $criteriaValues = '^' . $criteriaValues . '$';
  536. }
  537. if ('IN (...)' != $func_type
  538. && 'NOT IN (...)' != $func_type
  539. && 'BETWEEN' != $func_type
  540. && 'NOT BETWEEN' != $func_type
  541. ) {
  542. if ($func_type == 'LIKE %...%' || $func_type == 'LIKE') {
  543. $where = $backquoted_name . ' ' . $func_type . ' ' . $quot
  544. . PMA_Util::sqlAddSlashes($criteriaValues, true) . $quot;
  545. } else {
  546. $where = $backquoted_name . ' ' . $func_type . ' ' . $quot
  547. . PMA_Util::sqlAddSlashes($criteriaValues) . $quot;
  548. }
  549. return $where;
  550. }
  551. $func_type = str_replace(' (...)', '', $func_type);
  552. //Don't explode if this is already an array
  553. //(Case for (NOT) IN/BETWEEN.)
  554. if (is_array($criteriaValues)) {
  555. $values = $criteriaValues;
  556. } else {
  557. $values = explode(',', $criteriaValues);
  558. }
  559. // quote values one by one
  560. $emptyKey = false;
  561. foreach ($values as $key => &$value) {
  562. if ('' === $value) {
  563. $emptyKey = $key;
  564. $value = 'NULL';
  565. continue;
  566. }
  567. $value = $quot . PMA_Util::sqlAddSlashes(trim($value))
  568. . $quot;
  569. }
  570. if ('BETWEEN' == $func_type || 'NOT BETWEEN' == $func_type) {
  571. $where = $backquoted_name . ' ' . $func_type . ' '
  572. . (isset($values[0]) ? $values[0] : '')
  573. . ' AND ' . (isset($values[1]) ? $values[1] : '');
  574. } else { //[NOT] IN
  575. if (false !== $emptyKey) {
  576. unset($values[$emptyKey]);
  577. }
  578. $wheres = array();
  579. if (!empty($values)) {
  580. $wheres[] = $backquoted_name . ' ' . $func_type
  581. . ' (' . implode(',', $values) . ')';
  582. }
  583. if (false !== $emptyKey) {
  584. $wheres[] = $backquoted_name . ' IS NULL';
  585. }
  586. $where = implode(' OR ', $wheres);
  587. if (1 < count($wheres)) {
  588. $where = '(' . $where . ')';
  589. }
  590. }
  591. } // end if
  592. return $where;
  593. }
  594. /**
  595. * Builds the sql search query from the post parameters
  596. *
  597. * @return string the generated SQL query
  598. */
  599. public function buildSqlQuery()
  600. {
  601. $sql_query = 'SELECT ';
  602. // If only distinct values are needed
  603. $is_distinct = (isset($_POST['distinct'])) ? 'true' : 'false';
  604. if ($is_distinct == 'true') {
  605. $sql_query .= 'DISTINCT ';
  606. }
  607. // if all column names were selected to display, we do a 'SELECT *'
  608. // (more efficient and this helps prevent a problem in IE
  609. // if one of the rows is edited and we come back to the Select results)
  610. if (isset($_POST['zoom_submit']) || ! empty($_POST['displayAllColumns'])) {
  611. $sql_query .= '* ';
  612. } else {
  613. $sql_query .= implode(
  614. ', ',
  615. PMA_Util::backquote($_POST['columnsToDisplay'])
  616. );
  617. } // end if
  618. $sql_query .= ' FROM '
  619. . PMA_Util::backquote($_POST['table']);
  620. $whereClause = $this->_generateWhereClause();
  621. $sql_query .= $whereClause;
  622. // if the search results are to be ordered
  623. if (isset($_POST['orderByColumn']) && $_POST['orderByColumn'] != '--nil--') {
  624. $sql_query .= ' ORDER BY '
  625. . PMA_Util::backquote($_POST['orderByColumn'])
  626. . ' ' . $_POST['order'];
  627. } // end if
  628. return $sql_query;
  629. }
  630. /**
  631. * Generates the where clause for the SQL search query to be executed
  632. *
  633. * @return string the generated where clause
  634. */
  635. private function _generateWhereClause()
  636. {
  637. if (isset($_POST['customWhereClause'])
  638. && trim($_POST['customWhereClause']) != ''
  639. ) {
  640. return ' WHERE ' . $_POST['customWhereClause'];
  641. }
  642. // If there are no search criteria set or no unary criteria operators,
  643. // return
  644. if (! isset($_POST['criteriaValues'])
  645. && ! isset($_POST['criteriaColumnOperators'])
  646. ) {
  647. return '';
  648. }
  649. // else continue to form the where clause from column criteria values
  650. $fullWhereClause = $charsets = array();
  651. reset($_POST['criteriaColumnOperators']);
  652. while (list($column_index, $operator) = each(
  653. $_POST['criteriaColumnOperators']
  654. )) {
  655. list($charsets[$column_index]) = explode(
  656. '_', $_POST['criteriaColumnCollations'][$column_index]
  657. );
  658. $unaryFlag = $GLOBALS['PMA_Types']->isUnaryOperator($operator);
  659. $tmp_geom_func = isset($geom_func[$column_index])
  660. ? $geom_func[$column_index] : null;
  661. $whereClause = $this->_getWhereClause(
  662. $_POST['criteriaValues'][$column_index],
  663. $_POST['criteriaColumnNames'][$column_index],
  664. $_POST['criteriaColumnTypes'][$column_index],
  665. $_POST['criteriaColumnCollations'][$column_index],
  666. $operator,
  667. $unaryFlag,
  668. $tmp_geom_func
  669. );
  670. if ($whereClause) {
  671. $fullWhereClause[] = $whereClause;
  672. }
  673. } // end while
  674. if ($fullWhereClause) {
  675. return ' WHERE ' . implode(' AND ', $fullWhereClause);
  676. }
  677. return '';
  678. }
  679. /**
  680. * Generates HTML for a geometrical function column to be displayed in table
  681. * search selection form
  682. *
  683. * @param integer $column_index index of current column in $columnTypes array
  684. *
  685. * @return string the generated HTML
  686. */
  687. private function _getGeomFuncHtml($column_index)
  688. {
  689. $html_output = '';
  690. // return if geometrical column is not present
  691. if (! $this->_geomColumnFlag) {
  692. return $html_output;
  693. }
  694. /**
  695. * Displays 'Function' column if it is present
  696. */
  697. $html_output .= '<td>';
  698. $geom_types = PMA_Util::getGISDatatypes();
  699. // if a geometry column is present
  700. if (in_array($this->_columnTypes[$column_index], $geom_types)) {
  701. $html_output .= '<select class="geom_func" name="geom_func['
  702. . $column_index . ']">';
  703. // get the relevant list of GIS functions
  704. $funcs = PMA_Util::getGISFunctions(
  705. $this->_columnTypes[$column_index], true, true
  706. );
  707. /**
  708. * For each function in the list of functions,
  709. * add an option to select list
  710. */
  711. foreach ($funcs as $func_name => $func) {
  712. $name = isset($func['display']) ? $func['display'] : $func_name;
  713. $html_output .= '<option value="' . htmlspecialchars($name) . '">'
  714. . htmlspecialchars($name) . '</option>';
  715. }
  716. $html_output .= '</select>';
  717. } else {
  718. $html_output .= '&nbsp;';
  719. }
  720. $html_output .= '</td>';
  721. return $html_output;
  722. }
  723. /**
  724. * Generates formatted HTML for extra search options in table search form
  725. *
  726. * @return string the generated HTML
  727. */
  728. private function _getOptions()
  729. {
  730. $html_output = '';
  731. $html_output .= PMA_Util::getDivForSliderEffect(
  732. 'searchoptions', __('Options')
  733. );
  734. /**
  735. * Displays columns select list for selecting distinct columns in the search
  736. */
  737. $html_output .= '<fieldset id="fieldset_select_fields">'
  738. . '<legend>' . __('Select columns (at least one):') . '</legend>'
  739. . '<select name="columnsToDisplay[]"'
  740. . ' size="' . min(count($this->_columnNames), 10) . '"'
  741. . ' multiple="multiple">';
  742. // Displays the list of the fields
  743. foreach ($this->_columnNames as $each_field) {
  744. $html_output .= ' '
  745. . '<option value="' . htmlspecialchars($each_field) . '"'
  746. . ' selected="selected">' . htmlspecialchars($each_field)
  747. . '</option>' . "\n";
  748. } // end for
  749. $html_output .= '</select>'
  750. . '<input type="checkbox" name="distinct" value="DISTINCT"'
  751. . ' id="oDistinct" />'
  752. . '<label for="oDistinct">DISTINCT</label></fieldset>';
  753. /**
  754. * Displays input box for custom 'Where' clause to be used in the search
  755. */
  756. $html_output .= '<fieldset id="fieldset_search_conditions">'
  757. . '<legend>' . '<em>' . __('Or') . '</em> '
  758. . __('Add search conditions (body of the "where" clause):') . '</legend>';
  759. $html_output .= PMA_Util::showMySQLDocu('Functions');
  760. $html_output .= '<input type="text" name="customWhereClause"'
  761. . ' class="textfield" size="64" />';
  762. $html_output .= '</fieldset>';
  763. /**
  764. * Displays option of changing default number of rows displayed per page
  765. */
  766. $html_output .= '<fieldset id="fieldset_limit_rows">'
  767. . '<legend>' . __('Number of rows per page') . '</legend>'
  768. . '<input type="number" name="session_max_rows" required="required" '
  769. . 'min="1" '
  770. . 'value="' . $GLOBALS['cfg']['MaxRows'] . '" class="textfield" />'
  771. . '</fieldset>';
  772. /**
  773. * Displays option for ordering search results
  774. * by a column value (Asc or Desc)
  775. */
  776. $html_output .= '<fieldset id="fieldset_display_order">'
  777. . '<legend>' . __('Display order:') . '</legend>'
  778. . '<select name="orderByColumn"><option value="--nil--"></option>';
  779. foreach ($this->_columnNames as $each_field) {
  780. $html_output .= ' '
  781. . '<option value="' . htmlspecialchars($each_field) . '">'
  782. . htmlspecialchars($each_field) . '</option>' . "\n";
  783. } // end for
  784. $html_output .= '</select>';
  785. $choices = array(
  786. 'ASC' => __('Ascending'),
  787. 'DESC' => __('Descending')
  788. );
  789. $html_output .= PMA_Util::getRadioFields(
  790. 'order', $choices, 'ASC', false, true, "formelement"
  791. );
  792. unset($choices);
  793. $html_output .= '</fieldset><br style="clear: both;"/></div>';
  794. return $html_output;
  795. }
  796. /**
  797. * Other search criteria like data label
  798. * (for tbl_zoom_select.php)
  799. *
  800. * @param array $dataLabel Label for points in zoom plot
  801. *
  802. * @return string the generated html
  803. */
  804. private function _getOptionsZoom($dataLabel)
  805. {
  806. $html_output = '';
  807. $html_output .= '<table class="data">';
  808. //Select options for datalabel
  809. $html_output .= '<tr>';
  810. $html_output .= '<td><label for="dataLabel">'
  811. . __("Use this column to label each point") . '</label></td>';
  812. $html_output .= '<td><select name="dataLabel" id="dataLabel" >'
  813. . '<option value = "">' . __('None') . '</option>';
  814. for ($j = 0, $nb = count($this->_columnNames); $j < $nb; $j++) {
  815. if (isset($dataLabel)
  816. && $dataLabel == htmlspecialchars($this->_columnNames[$j])
  817. ) {
  818. $html_output .= '<option value="'
  819. . htmlspecialchars($this->_columnNames[$j])
  820. . '" selected="selected">'
  821. . htmlspecialchars($this->_columnNames[$j])
  822. . '</option>';
  823. } else {
  824. $html_output .= '<option value="'
  825. . htmlspecialchars($this->_columnNames[$j]) . '" >'
  826. . htmlspecialchars($this->_columnNames[$j]) . '</option>';
  827. }
  828. }
  829. $html_output .= '</select></td>';
  830. $html_output .= '</tr>';
  831. //Inputbox for changing default maximum rows to plot
  832. $html_output .= '<tr>';
  833. $html_output .= '<td><label for="maxRowPlotLimit">'
  834. . __("Maximum rows to plot") . '</label></td>';
  835. $html_output .= '<td>';
  836. $html_output .= '<input type="number" name="maxPlotLimit"'
  837. . ' id="maxRowPlotLimit" required="required"'
  838. . ' value="' . ((! empty($_POST['maxPlotLimit']))
  839. ? htmlspecialchars($_POST['maxPlotLimit'])
  840. : $GLOBALS['cfg']['maxRowPlotLimit'])
  841. . '" />';
  842. $html_output .= '</td></tr>';
  843. $html_output .= '</table>';
  844. return $html_output;
  845. }
  846. /**
  847. * Provides a column's type, collation, operators list, and crietria value
  848. * to display in table search form
  849. *
  850. * @param integer $search_index Row number in table search form
  851. * @param integer $column_index Column index in ColumnNames array
  852. *
  853. * @return array Array contaning column's properties
  854. */
  855. public function getColumnProperties($search_index, $column_index)
  856. {
  857. $selected_operator = (isset($_POST['criteriaColumnOperators'])
  858. ? $_POST['criteriaColumnOperators'][$search_index] : '');
  859. $entered_value = (isset($_POST['criteriaValues'])
  860. ? $_POST['criteriaValues'] : '');
  861. $titles = array(
  862. 'Browse' => PMA_Util::getIcon(
  863. 'b_browse.png', __('Browse foreign values')
  864. )
  865. );
  866. //Gets column's type and collation
  867. $type = $this->_columnTypes[$column_index];
  868. $collation = $this->_columnCollations[$column_index];
  869. //Gets column's comparison operators depending on column type
  870. $func = '<select name="criteriaColumnOperators['
  871. . $search_index . ']" onchange="changeValueFieldType(this, '
  872. . $search_index . ')">';
  873. $func .= $GLOBALS['PMA_Types']->getTypeOperatorsHtml(
  874. preg_replace('@\(.*@s', '', $this->_columnTypes[$column_index]),
  875. $this->_columnNullFlags[$column_index], $selected_operator
  876. );
  877. $func .= '</select>';
  878. //Gets link to browse foreign data(if any) and criteria inputbox
  879. $foreignData = PMA_getForeignData(
  880. $this->_foreigners, $this->_columnNames[$column_index], false, '', ''
  881. );
  882. $value = $this->_getInputbox(
  883. $foreignData, $this->_columnNames[$column_index], $type, $search_index,
  884. $titles, $GLOBALS['cfg']['ForeignKeyMaxLimit'], $entered_value
  885. );
  886. return array(
  887. 'type' => $type,
  888. 'collation' => $collation,
  889. 'func' => $func,
  890. 'value' => $value
  891. );
  892. }
  893. /**
  894. * Provides the search form's table row in case of Normal Search
  895. * (for tbl_select.php)
  896. *
  897. * @return string the generated table row
  898. */
  899. private function _getRowsNormal()
  900. {
  901. $odd_row = true;
  902. $html_output = '';
  903. // for every column present in table
  904. for (
  905. $column_index = 0, $nb = count($this->_columnNames);
  906. $column_index < $nb;
  907. $column_index++
  908. ) {
  909. $html_output .= '<tr class="noclick '
  910. . ($odd_row ? 'odd' : 'even')
  911. . '">';
  912. $odd_row = !$odd_row;
  913. //If 'Function' column is present
  914. $html_output .= $this->_getGeomFuncHtml($column_index);
  915. //Displays column's name, type, collation and value
  916. $html_output .= '<th>'
  917. . htmlspecialchars($this->_columnNames[$column_index]) . '</th>';
  918. $properties = $this->getColumnProperties($column_index, $column_index);
  919. $html_output .= '<td>' . $properties['type'] . '</td>';
  920. $html_output .= '<td>' . $properties['collation'] . '</td>';
  921. $html_output .= '<td>' . $properties['func'] . '</td>';
  922. // here, the data-type attribute is needed for a date/time picker
  923. $html_output .= '<td data-type="' . $properties['type'] . '"'
  924. . '>' . $properties['value'] . '</td>';
  925. $html_output .= '</tr>';
  926. //Displays hidden fields
  927. $html_output .= '<tr><td>';
  928. $html_output .= '<input type="hidden"'
  929. . ' name="criteriaColumnNames[' . $column_index . ']"'
  930. . ' value="' . htmlspecialchars($this->_columnNames[$column_index])
  931. . '" />';
  932. $html_output .= '<input type="hidden"'
  933. . ' name="criteriaColumnTypes[' . $column_index . ']"'
  934. . ' value="' . $this->_columnTypes[$column_index] . '" />';
  935. $html_output .= '<input type="hidden"'
  936. . ' name="criteriaColumnCollations[' . $column_index . ']"'
  937. . ' value="' . $this->_columnCollations[$column_index] . '" />';
  938. $html_output .= '</td></tr>';
  939. } // end for
  940. return $html_output;
  941. }
  942. /**
  943. * Provides the search form's table row in case of Zoom Search
  944. * (for tbl_zoom_select.php)
  945. *
  946. * @return string the generated table row
  947. */
  948. private function _getRowsZoom()
  949. {
  950. $odd_row = true;
  951. $html_output = '';
  952. $type = array();
  953. $collation = array();
  954. $func = array();
  955. $value = array();
  956. /**
  957. * Get already set search criteria (if any)
  958. */
  959. //Displays column rows for search criteria input
  960. for ($i = 0; $i < 4; $i++) {
  961. //After X-Axis and Y-Axis column rows, display additional criteria
  962. // option
  963. if ($i == 2) {
  964. $html_output .= '<tr><td>';
  965. $html_output .= __("Additional search criteria");
  966. $html_output .= '</td></tr>';
  967. }
  968. $html_output .= '<tr class="noclick '
  969. . ($odd_row ? 'odd' : 'even')
  970. . '">';
  971. $odd_row = ! $odd_row;
  972. //Select options for column names
  973. $html_output .= '<th><select name="criteriaColumnNames[]" id="'
  974. . 'tableid_' . $i . '" >';
  975. $html_output .= '<option value="' . 'pma_null' . '">' . __('None')
  976. . '</option>';
  977. for ($j = 0, $nb = count($this->_columnNames); $j < $nb; $j++) {
  978. if (isset($_POST['criteriaColumnNames'][$i])
  979. && $_POST['criteriaColumnNames'][$i] == htmlspecialchars($this->_columnNames[$j])
  980. ) {
  981. $html_output .= '<option value="'
  982. . htmlspecialchars($this->_columnNames[$j])
  983. . '" selected="selected">'
  984. . htmlspecialchars($this->_columnNames[$j])
  985. . '</option>';
  986. } else {
  987. $html_output .= '<option value="'
  988. . htmlspecialchars($this->_columnNames[$j]) . '">'
  989. . htmlspecialchars($this->_columnNames[$j]) . '</option>';
  990. }
  991. }
  992. $html_output .= '</select></th>';
  993. if (isset($_POST['criteriaColumnNames'])
  994. && $_POST['criteriaColumnNames'][$i] != 'pma_null'
  995. ) {
  996. $key = array_search(
  997. $_POST['criteriaColumnNames'][$i],
  998. $this->_columnNames
  999. );
  1000. $properties = $this->getColumnProperties($i, $key);
  1001. $type[$i] = $properties['type'];
  1002. $collation[$i] = $properties['collation'];
  1003. $func[$i] = $properties['func'];
  1004. $value[$i] = $properties['value'];
  1005. }
  1006. //Column type
  1007. $html_output .= '<td>' . (isset($type[$i]) ? $type[$i] : '') . '</td>';
  1008. //Column Collation
  1009. $html_output .= '<td>' . (isset($collation[$i]) ? $collation[$i] : '')
  1010. . '</td>';
  1011. //Select options for column operators
  1012. $html_output .= '<td>' . (isset($func[$i]) ? $func[$i] : '') . '</td>';
  1013. //Inputbox for search criteria value
  1014. $html_output .= '<td>' . (isset($value[$i]) ? $value[$i] : '') . '</td>';
  1015. $html_output .= '</tr>';
  1016. //Displays hidden fields
  1017. $html_output .= '<tr><td>';
  1018. $html_output
  1019. .= '<input type="hidden" name="criteriaColumnTypes[' . $i . ']"'
  1020. . ' id="types_' . $i . '" ';
  1021. if (isset($_POST['criteriaColumnTypes'][$i])) {
  1022. $html_output .= 'value="' . $_POST['criteriaColumnTypes'][$i] . '" ';
  1023. }
  1024. $html_output .= '/>';
  1025. $html_output .= '<input type="hidden" name="criteriaColumnCollations['
  1026. . $i . ']" id="collations_' . $i . '" />';
  1027. $html_output .= '</td></tr>';
  1028. }//end for
  1029. return $html_output;
  1030. }
  1031. /**
  1032. * Generates HTML for displaying fields table in search form
  1033. *
  1034. * @return string the generated HTML
  1035. */
  1036. private function _getFieldsTableHtml()
  1037. {
  1038. $html_output = '';
  1039. $html_output .= '<table class="data"'
  1040. . ($this->_searchType == 'zoom' ? ' id="tableFieldsId"' : '') . '>';
  1041. $html_output .= $this->_getTableHeader();
  1042. $html_output .= '<tbody>';
  1043. if ($this->_searchType == 'zoom') {
  1044. $html_output .= $this->_getRowsZoom();
  1045. } else {
  1046. $html_output .= $this->_getRowsNormal();
  1047. }
  1048. $html_output .= '</tbody></table>';
  1049. return $html_output;
  1050. }
  1051. /**
  1052. * Provides the form tag for table search form
  1053. * (normal search or zoom search)
  1054. *
  1055. * @param string $goto Goto URL
  1056. *
  1057. * @return string the HTML for form tag
  1058. */
  1059. private function _getFormTag($goto)
  1060. {
  1061. $html_output = '';
  1062. $scriptName = '';
  1063. $formId = '';
  1064. switch ($this->_searchType) {
  1065. case 'normal' :
  1066. $scriptName = 'tbl_select.php';
  1067. $formId = 'tbl_search_form';
  1068. break;
  1069. case 'zoom' :
  1070. $scriptName = 'tbl_zoom_select.php';
  1071. $formId = 'zoom_search_form';
  1072. break;
  1073. case 'replace' :
  1074. $scriptName = 'tbl_find_replace.php';
  1075. $formId = 'find_replace_form';
  1076. break;
  1077. }
  1078. $html_output .= '<form method="post" action="' . $scriptName . '" '
  1079. . 'name="insertForm" id="' . $formId . '" '
  1080. . 'class="ajax"' . '>';
  1081. $html_output .= PMA_URL_getHiddenInputs($this->_db, $this->_table);
  1082. $html_output .= '<input type="hidden" name="goto" value="' . $goto . '" />';
  1083. $html_output .= '<input type="hidden" name="back" value="' . $scriptName
  1084. . '" />';
  1085. return $html_output;
  1086. }
  1087. /**
  1088. * Returns the HTML for secondary levels tabs of the table search page
  1089. *
  1090. * @return string HTML for secondary levels tabs
  1091. */
  1092. public function getSecondaryTabs()
  1093. {
  1094. $url_params = array();
  1095. $url_params['db'] = $this->_db;
  1096. $url_params['table'] = $this->_table;
  1097. $html_output = '<ul id="topmenu2">';
  1098. foreach ($this->_getSubTabs() as $tab) {
  1099. $html_output .= PMA_Util::getHtmlTab($tab, $url_params);
  1100. }
  1101. $html_output .= '</ul>';
  1102. $html_output .= '<div class="clearfloat"></div>';
  1103. return $html_output;
  1104. }
  1105. /**
  1106. * Generates the table search form under table search tab
  1107. *
  1108. * @param string $goto Goto URL
  1109. * @param string $dataLabel Label for points in zoom plot
  1110. *
  1111. * @return string the generated HTML for table search form
  1112. */
  1113. public function getSelectionForm($goto, $dataLabel = null)
  1114. {
  1115. $html_output = $this->_getFormTag($goto);
  1116. if ($this->_searchType == 'zoom') {
  1117. $html_output .= '<fieldset id="fieldset_zoom_search">';
  1118. $html_output .= '<fieldset id="inputSection">';
  1119. $html_output .= '<legend>'
  1120. . __('Do a "query by example" (wildcard: "%") for two different columns')
  1121. . '</legend>';
  1122. $html_output .= $this->_getFieldsTableHtml();
  1123. $html_output .= $this->_getOptionsZoom($dataLabel);
  1124. $html_output .= '</fieldset>';
  1125. $html_output .= '</fieldset>';
  1126. } else if ($this->_searchType == 'normal') {
  1127. $html_output .= '<fieldset id="fieldset_table_search">';
  1128. $html_output .= '<fieldset id="fieldset_table_qbe">';
  1129. $html_output .= '<legend>'
  1130. . __('Do a "query by example" (wildcard: "%")')
  1131. . '</legend>';
  1132. $html_output .= $this->_getFieldsTableHtml();
  1133. $html_output .= '<div id="gis_editor"></div>';
  1134. $html_output .= '<div id="popup_background"></div>';
  1135. $html_output .= '</fieldset>';
  1136. $html_output .= $this->_getOptions();
  1137. $html_output .= '</fieldset>';
  1138. } else if ($this->_searchType == 'replace') {
  1139. $html_output .= '<fieldset id="fieldset_find_replace">';
  1140. $html_output .= '<fieldset id="fieldset_find">';
  1141. $html_output .= '<legend>' . __('Find and Replace') . '</legend>';
  1142. $html_output .= $this->_getSearchAndReplaceHTML();
  1143. $html_output .= '</fieldset>';
  1144. $html_output .= '</fieldset>';
  1145. }
  1146. /**
  1147. * Displays selection form's footer elements
  1148. */
  1149. $html_output .= '<fieldset class="tblFooters">';
  1150. $html_output .= '<input type="submit" name="'
  1151. . ($this->_searchType == 'zoom' ? 'zoom_submit' : 'submit')
  1152. . ($this->_searchType == 'zoom' ? '" id="inputFormSubmitId"' : '" ')
  1153. . 'value="' . __('Go') . '" />';
  1154. $html_output .= '</fieldset></form>';
  1155. $html_output .= '<div id="sqlqueryresults"></div>';
  1156. return $html_output;
  1157. }
  1158. /**
  1159. * Provides form for displaying point data and also the scatter plot
  1160. * (for tbl_zoom_select.php)
  1161. *
  1162. * @param string $goto Goto URL
  1163. * @param array $data Array containing SQL query data
  1164. *
  1165. * @return string form's html
  1166. */
  1167. public function getZoomResultsForm($goto, $data)
  1168. {
  1169. $html_output = '';
  1170. $titles = array(
  1171. 'Browse' => PMA_Util::getIcon(
  1172. 'b_browse.png',
  1173. __('Browse foreign values')
  1174. )
  1175. );
  1176. $html_output .= '<form method="post" action="tbl_zoom_select.php"'
  1177. . ' name="displayResultForm" id="zoom_display_form"'
  1178. . ' class="ajax"' . '>';
  1179. $html_output .= PMA_URL_getHiddenInputs($this->_db, $this->_table);
  1180. $html_output .= '<input type="hidden" name="goto" value="' . $goto . '" />';
  1181. $html_output
  1182. .= '<input type="hidden" name="back" value="tbl_zoom_select.php" />';
  1183. $html_output .= '<fieldset id="displaySection">';
  1184. $html_output .= '<legend>' . __('Browse/Edit the points') . '</legend>';
  1185. //JSON encode the data(query result)
  1186. $html_output .= '<center>';
  1187. if (isset($_POST['zoom_submit']) && ! empty($data)) {
  1188. $html_output .= '<div id="resizer">';
  1189. $html_output .= '<center><a href="#" onclick="displayHelp();">'
  1190. . __('How to use') . '</a></center>';
  1191. $html_output .= '<div id="querydata" style="display:none">'
  1192. . json_encode($data) . '</div>';
  1193. $html_output .= '<div id="querychart"></div>';
  1194. $html_output .= '<button class="button-reset">'
  1195. . __('Reset zoom') . '</button>';
  1196. $html_output .= '</div>';
  1197. }
  1198. $html_output .= '</center>';
  1199. //Displays rows in point edit form
  1200. $html_output .= '<div id="dataDisplay" style="display:none">';
  1201. $html_output .= '<table><thead>';
  1202. $html_output .= '<tr>';
  1203. $html_output .= '<th>' . __('Column') . '</th>'
  1204. . '<th>' . __('Null') . '</th>'
  1205. . '<th>' . __('Value') . '</th>';
  1206. $html_output .= '</tr>';
  1207. $html_output .= '</thead>';
  1208. $html_output .= '<tbody>';
  1209. $odd_row = true;
  1210. for (
  1211. $column_index = 0, $nb = count($this->_columnNames);
  1212. $column_index < $nb;
  1213. $column_index++
  1214. ) {
  1215. $fieldpopup = $this->_columnNames[$column_index];
  1216. $foreignData = PMA_getForeignData(
  1217. $this->_foreigners,
  1218. $fieldpopup,
  1219. false,
  1220. '',
  1221. ''
  1222. );
  1223. $html_output
  1224. .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
  1225. $odd_row = ! $odd_row;
  1226. //Display column Names
  1227. $html_output
  1228. .= '<th>' . htmlspecialchars($this->_columnNames[$column_index])
  1229. . '</th>';
  1230. //Null checkbox if column can be null
  1231. $html_output .= '<th>'
  1232. . (($this->_columnNullFlags[$column_index] == 'YES')
  1233. ? '<input type="checkbox" class="checkbox_null"'
  1234. . ' name="criteriaColumnNullFlags[' . $column_index . ']"'
  1235. . ' id="edit_fields_null_id_' . $column_index . '" />'
  1236. : '');
  1237. $html_output .= '</th>';
  1238. //Column's Input box
  1239. $html_output .= '<th>';
  1240. $html_output .= $this->_getInputbox(
  1241. $foreignData, $fieldpopup, $this->_columnTypes[$column_index],
  1242. $column_index, $titles, $GLOBALS['cfg']['ForeignKeyMaxLimit'],
  1243. '', false, true
  1244. );
  1245. $html_output .= '</th></tr>';
  1246. }
  1247. $html_output .= '</tbody></table>';
  1248. $html_output .= '</div>';
  1249. $html_output .= '<input type="hidden" id="queryID" name="sql_query" />';
  1250. $html_output .= '</form>';
  1251. return $html_output;
  1252. }
  1253. /**
  1254. * Displays the 'Find and Replace' form
  1255. *
  1256. * @return string HTML for 'Find and Replace' form
  1257. */
  1258. function _getSearchAndReplaceHTML()
  1259. {
  1260. $htmlOutput = __('Find:')
  1261. . '<input type="text" value="" name="find" required />';
  1262. $htmlOutput .= __('Replace with:')
  1263. . '<input type="text" value="" name="replaceWith" required />';
  1264. $htmlOutput .= __('Column:') . '<select name="columnIndex">';
  1265. for ($i = 0, $nb = count($this->_columnNames); $i < $nb; $i++) {
  1266. $type = preg_replace('@\(.*@s', '', $this->_columnTypes[$i]);
  1267. if ($GLOBALS['PMA_Types']->getTypeClass($type) == 'CHAR') {
  1268. $column = $this->_columnNames[$i];
  1269. $htmlOutput .= '<option value="' . $i . '">'
  1270. . htmlspecialchars($column) . '</option>';
  1271. }
  1272. }
  1273. $htmlOutput .= '</select>';
  1274. return $htmlOutput;
  1275. }
  1276. /**
  1277. * Returns HTML for prviewing strings found and their replacements
  1278. *
  1279. * @param int $columnIndex index of the column
  1280. * @param string $find string to find in the column
  1281. * @param string $replaceWith string to replace with
  1282. * @param string $charSet character set of the connection
  1283. *
  1284. * @return string HTML for prviewing strings found and their replacements
  1285. */
  1286. function getReplacePreview($columnIndex, $find, $replaceWith, $charSet)
  1287. {
  1288. $column = $this->_columnNames[$columnIndex];
  1289. $sql_query = "SELECT "
  1290. . PMA_Util::backquote($column) . ","
  1291. . " REPLACE("
  1292. . PMA_Util::backquote($column) . ", '" . $find . "', '" . $replaceWith
  1293. . "'),"
  1294. . " COUNT(*)"
  1295. . " FROM " . PMA_Util::backquote($this->_db)
  1296. . "." . PMA_Util::backquote($this->_table)
  1297. . " WHERE " . PMA_Util::backquote($column)
  1298. . " LIKE '%" . $find . "%' COLLATE " . $charSet . "_bin"; // here we
  1299. // change the collation of the 2nd operand to a case sensitive
  1300. // binary collation to make sure that the comparison is case sensitive
  1301. $sql_query .= " GROUP BY " . PMA_Util::backquote($column)
  1302. . " ORDER BY " . PMA_Util::backquote($column) . " ASC";
  1303. $resultSet = $GLOBALS['dbi']->query(
  1304. $sql_query, null, PMA_DatabaseInterface::QUERY_STORE
  1305. );
  1306. $htmlOutput = '<form method="post" action="tbl_find_replace.php"'
  1307. . ' name="previewForm" id="previewForm" class="ajax">';
  1308. $htmlOutput .= PMA_URL_getHiddenInputs($this->_db, $this->_table);
  1309. $htmlOutput .= '<input type="hidden" name="replace" value="true" />';
  1310. $htmlOutput .= '<input type="hidden" name="columnIndex" value="'
  1311. . $columnIndex . '" />';
  1312. $htmlOutput .= '<input type="hidden" name="findString"'
  1313. . ' value="' . $find . '" />';
  1314. $htmlOutput .= '<input type="hidden" name="replaceWith"'
  1315. . ' value="' . $replaceWith . '" />';
  1316. $htmlOutput .= '<fieldset id="fieldset_find_replace_preview">';
  1317. $htmlOutput .= '<legend>' . __('Find and replace - preview') . '</legend>';
  1318. $htmlOutput .= '<table id="previewTable">'
  1319. . '<thead><tr>'
  1320. . '<th>' . __('Count') . '</th>'
  1321. . '<th>' . __('Original string') . '</th>'
  1322. . '<th>' . __('Replaced string') . '</th>'
  1323. . '</tr></thead>';
  1324. $htmlOutput .= '<tbody>';
  1325. $odd = true;
  1326. while ($row = $GLOBALS['dbi']->fetchRow($resultSet)) {
  1327. $val = $row[0];
  1328. $replaced = $row[1];
  1329. $count = $row[2];
  1330. $htmlOutput .= '<tr class="' . ($odd ? 'odd' : 'even') . '">';
  1331. $htmlOutput .= '<td class="right">' . htmlspecialchars($count) . '</td>';
  1332. $htmlOutput .= '<td>' . htmlspecialchars($val) . '</td>';
  1333. $htmlOutput .= '<td>' . htmlspecialchars($replaced) . '</td>';
  1334. $htmlOutput .= '</tr>';
  1335. $odd = ! $odd;
  1336. }
  1337. $htmlOutput .= '</tbody>';
  1338. $htmlOutput .= '</table>';
  1339. $htmlOutput .= '</fieldset>';
  1340. $htmlOutput .= '<fieldset class="tblFooters">';
  1341. $htmlOutput .= '<input type="submit" name="replace"'
  1342. . ' value="' . __('Replace') . '" />';
  1343. $htmlOutput .= '</fieldset>';
  1344. $htmlOutput .= '</form>';
  1345. return $htmlOutput;
  1346. }
  1347. /**
  1348. * Replaces a given string in a column with a give replacement
  1349. *
  1350. * @param int $columnIndex index of the column
  1351. * @param string $find string to find in the column
  1352. * @param string $replaceWith string to replace with
  1353. * @param string $charSet character set of the connection
  1354. *
  1355. * @return void
  1356. */
  1357. function replace($columnIndex, $find, $replaceWith, $charSet)
  1358. {
  1359. $column = $this->_columnNames[$columnIndex];
  1360. $sql_query = "UPDATE " . PMA_Util::backquote($this->_db)
  1361. . "." . PMA_Util::backquote($this->_table)
  1362. . " SET " . PMA_Util::backquote($column) . " ="
  1363. . " REPLACE("
  1364. . PMA_Util::backquote($column) . ", '" . $find . "', '" . $replaceWith
  1365. . "')"
  1366. . " WHERE " . PMA_Util::backquote($column)
  1367. . " LIKE '%" . $find . "%' COLLATE " . $charSet . "_bin"; // here we
  1368. // change the collation of the 2nd operand to a case sensitive
  1369. // binary collation to make sure that the comparison is case sensitive
  1370. $GLOBALS['dbi']->query(
  1371. $sql_query, null, PMA_DatabaseInterface::QUERY_STORE
  1372. );
  1373. $GLOBALS['sql_query'] = $sql_query;
  1374. }
  1375. }
  1376. ?>