PageRenderTime 71ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/phpmyadmin/libraries/DisplayResults.class.php

https://bitbucket.org/openemr/openemr
PHP | 5769 lines | 3321 code | 858 blank | 1590 comment | 601 complexity | 7abde8c08b12edb0e43917c75c4d827e MD5 | raw file
Possible License(s): Apache-2.0, AGPL-1.0, GPL-2.0, LGPL-3.0, BSD-3-Clause, Unlicense, MPL-2.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Hold the PMA_DisplayResults class
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. require_once './libraries/transformations.lib.php';
  12. /**
  13. * Handle all the functionalities related to displaying results
  14. * of sql queries, stored procedure, browsing sql processes or
  15. * displaying binary log.
  16. *
  17. * @package PhpMyAdmin
  18. */
  19. class PMA_DisplayResults
  20. {
  21. // Define constants
  22. const NO_EDIT_OR_DELETE = 'nn';
  23. const UPDATE_ROW = 'ur';
  24. const DELETE_ROW = 'dr';
  25. const KILL_PROCESS = 'kp';
  26. const POSITION_LEFT = 'left';
  27. const POSITION_RIGHT = 'right';
  28. const POSITION_BOTH = 'both';
  29. const POSITION_NONE = 'none';
  30. const PLACE_TOP_DIRECTION_DROPDOWN = 'top_direction_dropdown';
  31. const PLACE_BOTTOM_DIRECTION_DROPDOWN = 'bottom_direction_dropdown';
  32. const DISPLAY_FULL_TEXT = 'F';
  33. const DISPLAY_PARTIAL_TEXT = 'P';
  34. const HEADER_FLIP_TYPE_AUTO = 'auto';
  35. const HEADER_FLIP_TYPE_CSS = 'css';
  36. const HEADER_FLIP_TYPE_FAKE = 'fake';
  37. const DATE_FIELD = 'date';
  38. const DATETIME_FIELD = 'datetime';
  39. const TIMESTAMP_FIELD = 'timestamp';
  40. const TIME_FIELD = 'time';
  41. const STRING_FIELD = 'string';
  42. const GEOMETRY_FIELD = 'geometry';
  43. const BLOB_FIELD = 'BLOB';
  44. const BINARY_FIELD = 'BINARY';
  45. const RELATIONAL_KEY = 'K';
  46. const RELATIONAL_DISPLAY_COLUMN = 'D';
  47. const GEOMETRY_DISP_GEOM = 'GEOM';
  48. const GEOMETRY_DISP_WKT = 'WKT';
  49. const GEOMETRY_DISP_WKB = 'WKB';
  50. const SMART_SORT_ORDER = 'SMART';
  51. const ASCENDING_SORT_DIR = 'ASC';
  52. const DESCENDING_SORT_DIR = 'DESC';
  53. const TABLE_TYPE_INNO_DB = 'InnoDB';
  54. const ALL_ROWS = 'all';
  55. const QUERY_TYPE_SELECT = 'SELECT';
  56. const ROUTINE_PROCEDURE = 'procedure';
  57. const ROUTINE_FUNCTION = 'function';
  58. const ACTION_LINK_CONTENT_ICONS = 'icons';
  59. const ACTION_LINK_CONTENT_TEXT = 'text';
  60. // Declare global fields
  61. /** array with properties of the class */
  62. private $_property_array = array(
  63. /** string Database name */
  64. 'db' => null,
  65. /** string Table name */
  66. 'table' => null,
  67. /** string the URL to go back in case of errors */
  68. 'goto' => null,
  69. /** string the SQL query */
  70. 'sql_query' => null,
  71. /**
  72. * integer the total number of rows returned by the SQL query without any
  73. * appended "LIMIT" clause programmatically
  74. */
  75. 'unlim_num_rows' => null,
  76. /** array meta information about fields */
  77. 'fields_meta' => null,
  78. /** boolean */
  79. 'is_count' => null,
  80. /** integer */
  81. 'is_export' => null,
  82. /** boolean */
  83. 'is_func' => null,
  84. /** integer */
  85. 'is_analyse' => null,
  86. /** integer the total number of rows returned by the SQL query */
  87. 'num_rows' => null,
  88. /** integer the total number of fields returned by the SQL query */
  89. 'fields_cnt' => null,
  90. /** double time taken for execute the SQL query */
  91. 'querytime' => null,
  92. /** string path for theme images directory */
  93. 'pma_theme_image' => null,
  94. /** string */
  95. 'text_dir' => null,
  96. /** boolean */
  97. 'is_maint' => null,
  98. /** boolean */
  99. 'is_explain' => null,
  100. /** boolean */
  101. 'is_show' => null,
  102. /** boolean */
  103. 'is_browse_distinct' => null,
  104. /** array table definitions */
  105. 'showtable' => null,
  106. /** string */
  107. 'printview' => null,
  108. /** string URL query */
  109. 'url_query' => null,
  110. /** array column names to highlight */
  111. 'highlight_columns' => null,
  112. /** array holding various display information */
  113. 'display_params' => null,
  114. /** array mime types information of fields */
  115. 'mime_map' => null,
  116. /** boolean */
  117. 'editable' => null,
  118. /** random unique ID to distinguish result set */
  119. 'unique_id' => null,
  120. /** where clauses for each row, each table in the row */
  121. 'whereClauseMap' => array(),
  122. );
  123. /**
  124. * This variable contains the column transformation information
  125. * for some of the system databases.
  126. * One element of this array represent all relevant columns in all tables in
  127. * one specific database
  128. */
  129. public $transformation_info;
  130. /**
  131. * Get any property of this class
  132. *
  133. * @param string $property name of the property
  134. *
  135. * @return mixed|void if property exist, value of the relevant property
  136. */
  137. public function __get($property)
  138. {
  139. if (array_key_exists($property, $this->_property_array)) {
  140. return $this->_property_array[$property];
  141. }
  142. }
  143. /**
  144. * Set values for any property of this class
  145. *
  146. * @param string $property name of the property
  147. * @param mixed $value value to set
  148. *
  149. * @return void
  150. */
  151. public function __set($property, $value)
  152. {
  153. if (array_key_exists($property, $this->_property_array)) {
  154. $this->_property_array[$property] = $value;
  155. }
  156. }
  157. /**
  158. * Constructor for PMA_DisplayResults class
  159. *
  160. * @param string $db the database name
  161. * @param string $table the table name
  162. * @param string $goto the URL to go back in case of errors
  163. * @param string $sql_query the SQL query
  164. *
  165. * @access public
  166. */
  167. public function __construct($db, $table, $goto, $sql_query)
  168. {
  169. $this->_setDefaultTransformations();
  170. $this->__set('db', $db);
  171. $this->__set('table', $table);
  172. $this->__set('goto', $goto);
  173. $this->__set('sql_query', $sql_query);
  174. $this->__set('unique_id', rand());
  175. }
  176. /**
  177. * Sets default transformations for some columns
  178. *
  179. * @return void
  180. */
  181. private function _setDefaultTransformations()
  182. {
  183. $json_highlighting_data = array(
  184. 'libraries/plugins/transformations/output/Text_Plain_Json.class.php',
  185. 'Text_Plain_Json',
  186. 'Text_Plain'
  187. );
  188. $sql_highlighting_data = array(
  189. 'libraries/plugins/transformations/output/Text_Plain_Sql.class.php',
  190. 'Text_Plain_Sql',
  191. 'Text_Plain'
  192. );
  193. $blob_sql_highlighting_data = array(
  194. 'libraries/plugins/transformations/output/Text_Octetstream_Sql.class.php',
  195. 'Text_Octetstream_Sql',
  196. 'Text_Octetstream'
  197. );
  198. $link_data = array(
  199. 'libraries/plugins/transformations/Text_Plain_Link.class.php',
  200. 'Text_Plain_Link',
  201. 'Text_Plain'
  202. );
  203. $this->transformation_info = array(
  204. 'information_schema' => array(
  205. 'events' => array(
  206. 'event_definition' => $sql_highlighting_data
  207. ),
  208. 'processlist' => array(
  209. 'info' => $sql_highlighting_data
  210. ),
  211. 'routines' => array(
  212. 'routine_definition' => $sql_highlighting_data
  213. ),
  214. 'triggers' => array(
  215. 'action_statement' => $sql_highlighting_data
  216. ),
  217. 'views' => array(
  218. 'view_definition' => $sql_highlighting_data
  219. )
  220. ),
  221. 'mysql' => array(
  222. 'event' => array(
  223. 'body' => $blob_sql_highlighting_data,
  224. 'body_utf8' => $blob_sql_highlighting_data
  225. ),
  226. 'general_log' => array(
  227. 'argument' => $sql_highlighting_data
  228. ),
  229. 'help_category' => array(
  230. 'url' => $link_data
  231. ),
  232. 'help_topic' => array(
  233. 'example' => $sql_highlighting_data,
  234. 'url' => $link_data
  235. ),
  236. 'proc' => array(
  237. 'param_list' => $blob_sql_highlighting_data,
  238. 'returns' => $blob_sql_highlighting_data,
  239. 'body' => $blob_sql_highlighting_data,
  240. 'body_utf8' => $blob_sql_highlighting_data
  241. ),
  242. 'slow_log' => array(
  243. 'sql_text' => $sql_highlighting_data
  244. )
  245. )
  246. );
  247. $cfgRelation = PMA_getRelationsParam();
  248. if ($cfgRelation['db']) {
  249. $this->transformation_info[$cfgRelation['db']] = array();
  250. $relDb = &$this->transformation_info[$cfgRelation['db']];
  251. if (! empty($cfgRelation['history'])) {
  252. $relDb[$cfgRelation['history']] = array(
  253. 'sqlquery' => $sql_highlighting_data
  254. );
  255. }
  256. if (! empty($cfgRelation['bookmark'])) {
  257. $relDb[$cfgRelation['bookmark']] = array(
  258. 'query' => $sql_highlighting_data
  259. );
  260. }
  261. if (! empty($cfgRelation['tracking'])) {
  262. $relDb[$cfgRelation['tracking']] = array(
  263. 'schema_sql' => $sql_highlighting_data,
  264. 'data_sql' => $sql_highlighting_data
  265. );
  266. }
  267. if (! empty($cfgRelation['favorite'])) {
  268. $relDb[$cfgRelation['favorite']] = array(
  269. 'tables' => $json_highlighting_data
  270. );
  271. }
  272. if (! empty($cfgRelation['recent'])) {
  273. $relDb[$cfgRelation['recent']] = array(
  274. 'tables' => $json_highlighting_data
  275. );
  276. }
  277. if (! empty($cfgRelation['savedsearches'])) {
  278. $relDb[$cfgRelation['savedsearches']] = array(
  279. 'search_data' => $json_highlighting_data
  280. );
  281. }
  282. if (! empty($cfgRelation['designer_settings'])) {
  283. $relDb[$cfgRelation['designer_settings']] = array(
  284. 'settings_data' => $json_highlighting_data
  285. );
  286. }
  287. if (! empty($cfgRelation['table_uiprefs'])) {
  288. $relDb[$cfgRelation['table_uiprefs']] = array(
  289. 'prefs' => $json_highlighting_data
  290. );
  291. }
  292. if (! empty($cfgRelation['userconfig'])) {
  293. $relDb[$cfgRelation['userconfig']] = array(
  294. 'config_data' => $json_highlighting_data
  295. );
  296. }
  297. if (! empty($cfgRelation['export_templates'])) {
  298. $relDb[$cfgRelation['export_templates']] = array(
  299. 'template_data' => $json_highlighting_data
  300. );
  301. }
  302. }
  303. }
  304. /**
  305. * Set properties which were not initialized at the constructor
  306. *
  307. * @param integer $unlim_num_rows the total number of rows returned by
  308. * the SQL query without any appended
  309. * "LIMIT" clause programmatically
  310. * @param array $fields_meta meta information about fields
  311. * @param boolean $is_count statement is SELECT COUNT
  312. * @param integer $is_export statement contains INTO OUTFILE
  313. * @param boolean $is_func statement contains a function like SUM()
  314. * @param integer $is_analyse statement contains PROCEDURE ANALYSE
  315. * @param integer $num_rows total no. of rows returned by SQL query
  316. * @param integer $fields_cnt total no.of fields returned by SQL query
  317. * @param double $querytime time taken for execute the SQL query
  318. * @param string $pmaThemeImage path for theme images directory
  319. * @param string $text_dir text direction
  320. * @param boolean $is_maint statement contains a maintenance command
  321. * @param boolean $is_explain statement contains EXPLAIN
  322. * @param boolean $is_show statement contains SHOW
  323. * @param array $showtable table definitions
  324. * @param string $printview print view was requested
  325. * @param string $url_query URL query
  326. * @param boolean $editable whether the results set is editable
  327. * @param boolean $is_browse_dist whether browsing distinct values
  328. *
  329. * @return void
  330. *
  331. * @see sql.php
  332. */
  333. public function setProperties(
  334. $unlim_num_rows, $fields_meta, $is_count, $is_export, $is_func,
  335. $is_analyse, $num_rows, $fields_cnt, $querytime, $pmaThemeImage, $text_dir,
  336. $is_maint, $is_explain, $is_show, $showtable, $printview, $url_query,
  337. $editable, $is_browse_dist
  338. ) {
  339. $this->__set('unlim_num_rows', $unlim_num_rows);
  340. $this->__set('fields_meta', $fields_meta);
  341. $this->__set('is_count', $is_count);
  342. $this->__set('is_export', $is_export);
  343. $this->__set('is_func', $is_func);
  344. $this->__set('is_analyse', $is_analyse);
  345. $this->__set('num_rows', $num_rows);
  346. $this->__set('fields_cnt', $fields_cnt);
  347. $this->__set('querytime', $querytime);
  348. $this->__set('pma_theme_image', $pmaThemeImage);
  349. $this->__set('text_dir', $text_dir);
  350. $this->__set('is_maint', $is_maint);
  351. $this->__set('is_explain', $is_explain);
  352. $this->__set('is_show', $is_show);
  353. $this->__set('showtable', $showtable);
  354. $this->__set('printview', $printview);
  355. $this->__set('url_query', $url_query);
  356. $this->__set('editable', $editable);
  357. $this->__set('is_browse_distinct', $is_browse_dist);
  358. } // end of the 'setProperties()' function
  359. /**
  360. * Defines the parts to display for a print view
  361. *
  362. * @param array $displayParts the parts to display
  363. *
  364. * @return array $displayParts the modified display parts
  365. *
  366. * @access private
  367. *
  368. */
  369. private function _setDisplayPartsForPrintView($displayParts)
  370. {
  371. // set all elements to false!
  372. $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link
  373. $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link
  374. $displayParts['sort_lnk'] = (string) '0';
  375. $displayParts['nav_bar'] = (string) '0';
  376. $displayParts['bkm_form'] = (string) '0';
  377. $displayParts['text_btn'] = (string) '0';
  378. $displayParts['pview_lnk'] = (string) '0';
  379. return $displayParts;
  380. }
  381. /**
  382. * Defines the parts to display for a SHOW statement
  383. *
  384. * @param array $displayParts the parts to display
  385. *
  386. * @return array $displayParts the modified display parts
  387. *
  388. * @access private
  389. *
  390. */
  391. private function _setDisplayPartsForShow($displayParts)
  392. {
  393. preg_match(
  394. '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
  395. . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
  396. . ')@i',
  397. $this->__get('sql_query'), $which
  398. );
  399. $bIsProcessList = isset($which[1]);
  400. if ($bIsProcessList) {
  401. $str = ' ' . strtoupper($which[1]);
  402. $bIsProcessList = $bIsProcessList
  403. && strpos($str, 'PROCESSLIST') > 0;
  404. }
  405. if ($bIsProcessList) {
  406. // no edit link
  407. $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
  408. // "kill process" type edit link
  409. $displayParts['del_lnk'] = self::KILL_PROCESS;
  410. } else {
  411. // Default case -> no links
  412. // no edit link
  413. $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
  414. // no delete link
  415. $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
  416. }
  417. // Other settings
  418. $displayParts['sort_lnk'] = (string) '0';
  419. $displayParts['nav_bar'] = (string) '0';
  420. $displayParts['bkm_form'] = (string) '1';
  421. $displayParts['text_btn'] = (string) '1';
  422. $displayParts['pview_lnk'] = (string) '1';
  423. return $displayParts;
  424. }
  425. /**
  426. * Defines the parts to display for statements not related to data
  427. *
  428. * @param array $displayParts the parts to display
  429. *
  430. * @return array $displayParts the modified display parts
  431. *
  432. * @access private
  433. *
  434. */
  435. private function _setDisplayPartsForNonData($displayParts)
  436. {
  437. // Statement is a "SELECT COUNT", a
  438. // "CHECK/ANALYZE/REPAIR/OPTIMIZE/CHECKSUM", an "EXPLAIN" one or
  439. // contains a "PROC ANALYSE" part
  440. $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link
  441. $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link
  442. $displayParts['sort_lnk'] = (string) '0';
  443. $displayParts['nav_bar'] = (string) '0';
  444. $displayParts['bkm_form'] = (string) '1';
  445. if ($this->__get('is_maint')) {
  446. $displayParts['text_btn'] = (string) '1';
  447. } else {
  448. $displayParts['text_btn'] = (string) '0';
  449. }
  450. $displayParts['pview_lnk'] = (string) '1';
  451. return $displayParts;
  452. }
  453. /**
  454. * Defines the parts to display for other statements (probably SELECT)
  455. *
  456. * @param array $displayParts the parts to display
  457. *
  458. * @return array $displayParts the modified display parts
  459. *
  460. * @access private
  461. *
  462. */
  463. private function _setDisplayPartsForSelect($displayParts)
  464. {
  465. // Other statements (ie "SELECT" ones) -> updates
  466. // $displayParts['edit_lnk'], $displayParts['del_lnk'] and
  467. // $displayParts['text_btn'] (keeps other default values)
  468. $fields_meta = $this->__get('fields_meta');
  469. $prev_table = '';
  470. $displayParts['text_btn'] = (string) '1';
  471. $number_of_columns = $this->__get('fields_cnt');
  472. for ($i = 0; $i < $number_of_columns; $i++) {
  473. $is_link = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  474. || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
  475. || ($displayParts['sort_lnk'] != '0');
  476. // Displays edit/delete/sort/insert links?
  477. if ($is_link
  478. && $prev_table != ''
  479. && $fields_meta[$i]->table != ''
  480. && $fields_meta[$i]->table != $prev_table
  481. ) {
  482. // don't display links
  483. $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
  484. $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
  485. /**
  486. * @todo May be problematic with same field names
  487. * in two joined table.
  488. */
  489. // $displayParts['sort_lnk'] = (string) '0';
  490. if ($displayParts['text_btn'] == '1') {
  491. break;
  492. }
  493. } // end if
  494. // Always display print view link
  495. $displayParts['pview_lnk'] = (string) '1';
  496. if ($fields_meta[$i]->table != '') {
  497. $prev_table = $fields_meta[$i]->table;
  498. }
  499. } // end for
  500. return $displayParts;
  501. }
  502. /**
  503. * Defines the parts to display for the results of a SQL query
  504. *
  505. * @param array $displayParts the parts to display (see a few
  506. * lines above for explanations)
  507. * @param integer &$the_total the total number of rows returned by the SQL
  508. * query without any programmatically appended
  509. * LIMIT clause
  510. * (just a copy of $unlim_num_rows if it exists,
  511. * elsecomputed inside this function)
  512. *
  513. * @return array an array with explicit indexes for all the display
  514. * elements
  515. *
  516. * @access private
  517. *
  518. * @see getTable()
  519. */
  520. private function _setDisplayParts($displayParts, &$the_total)
  521. {
  522. // 1. Following variables are needed for use in isset/empty or
  523. // use with array indexes or safe use in foreach
  524. $db = $this->__get('db');
  525. $table = $this->__get('table');
  526. $unlim_num_rows = $this->__get('unlim_num_rows');
  527. $num_rows = $this->__get('num_rows');
  528. $printview = $this->__get('printview');
  529. // 2. Updates the display parts
  530. if ($printview == '1') {
  531. $displayParts = $this->_setDisplayPartsForPrintView($displayParts);
  532. } elseif ($this->__get('is_count') || $this->__get('is_analyse')
  533. || $this->__get('is_maint') || $this->__get('is_explain')
  534. ) {
  535. $displayParts = $this->_setDisplayPartsForNonData($displayParts);
  536. } elseif ($this->__get('is_show')) {
  537. $displayParts = $this->_setDisplayPartsForShow($displayParts);
  538. } else {
  539. $displayParts = $this->_setDisplayPartsForSelect($displayParts);
  540. } // end if..elseif...else
  541. // 3. Gets the total number of rows if it is unknown
  542. if (isset($unlim_num_rows) && $unlim_num_rows != '') {
  543. $the_total = $unlim_num_rows;
  544. } elseif ((($displayParts['nav_bar'] == '1')
  545. || ($displayParts['sort_lnk'] == '1'))
  546. && (/*overload*/mb_strlen($db) && !empty($table))
  547. ) {
  548. $the_total = $GLOBALS['dbi']->getTable($db, $table)->countRecords();
  549. }
  550. // if for COUNT query, number of rows returned more than 1
  551. // (may be being used GROUP BY)
  552. if ($this->__get('is_count') && isset($num_rows) && $num_rows > 1) {
  553. $displayParts['nav_bar'] = (string) '1';
  554. $displayParts['sort_lnk'] = (string) '1';
  555. }
  556. // 4. If navigation bar or sorting fields names URLs should be
  557. // displayed but there is only one row, change these settings to
  558. // false
  559. if ($displayParts['nav_bar'] == '1' || $displayParts['sort_lnk'] == '1') {
  560. // - Do not display sort links if less than 2 rows.
  561. // - For a VIEW we (probably) did not count the number of rows
  562. // so don't test this number here, it would remove the possibility
  563. // of sorting VIEW results.
  564. $_table = new PMA_Table($table, $db);
  565. if (isset($unlim_num_rows)
  566. && ($unlim_num_rows < 2)
  567. && ! $_table->isView()
  568. ) {
  569. $displayParts['sort_lnk'] = (string) '0';
  570. }
  571. } // end if (3)
  572. return $displayParts;
  573. } // end of the 'setDisplayParts()' function
  574. /**
  575. * Return true if we are executing a query in the form of
  576. * "SELECT * FROM <a table> ..."
  577. *
  578. * @param array $analyzed_sql_results analyzed sql results
  579. *
  580. * @return boolean
  581. *
  582. * @access private
  583. *
  584. * @see _getTableHeaders(), _getColumnParams()
  585. */
  586. private function _isSelect($analyzed_sql_results)
  587. {
  588. return ! ($this->__get('is_count')
  589. || $this->__get('is_export')
  590. || $this->__get('is_func')
  591. || $this->__get('is_analyse'))
  592. && !empty($analyzed_sql_results['select_from'])
  593. && !empty($analyzed_sql_results['statement']->from)
  594. && (count($analyzed_sql_results['statement']->from) == 1)
  595. && !empty($analyzed_sql_results['statement']->from[0]->table);
  596. }
  597. /**
  598. * Get a navigation button
  599. *
  600. * @param string $caption iconic caption for button
  601. * @param string $title text for button
  602. * @param integer $pos position for next query
  603. * @param string $html_sql_query query ready for display
  604. * @param boolean $back whether 'begin' or 'previous'
  605. * @param string $onsubmit optional onsubmit clause
  606. * @param string $input_for_real_end optional hidden field for special treatment
  607. * @param string $onclick optional onclick clause
  608. *
  609. * @return string html content
  610. *
  611. * @access private
  612. *
  613. * @see _getMoveBackwardButtonsForTableNavigation(),
  614. * _getMoveForwardButtonsForTableNavigation()
  615. */
  616. private function _getTableNavigationButton(
  617. $caption, $title, $pos, $html_sql_query, $back, $onsubmit = '',
  618. $input_for_real_end = '', $onclick = ''
  619. ) {
  620. $caption_output = '';
  621. if ($back) {
  622. if (PMA_Util::showIcons('TableNavigationLinksMode')) {
  623. $caption_output .= $caption;
  624. }
  625. if (PMA_Util::showText('TableNavigationLinksMode')) {
  626. $caption_output .= '&nbsp;' . $title;
  627. }
  628. } else {
  629. if (PMA_Util::showText('TableNavigationLinksMode')) {
  630. $caption_output .= $title;
  631. }
  632. if (PMA_Util::showIcons('TableNavigationLinksMode')) {
  633. $caption_output .= '&nbsp;' . $caption;
  634. }
  635. }
  636. $title_output = ' title="' . $title . '"';
  637. return '<td>'
  638. . '<form action="sql.php" method="post" ' . $onsubmit . '>'
  639. . PMA_URL_getHiddenInputs(
  640. $this->__get('db'), $this->__get('table')
  641. )
  642. . '<input type="hidden" name="sql_query" value="'
  643. . $html_sql_query . '" />'
  644. . '<input type="hidden" name="pos" value="' . $pos . '" />'
  645. . '<input type="hidden" name="is_browse_distinct" value="'
  646. . $this->__get('is_browse_distinct') . '" />'
  647. . '<input type="hidden" name="goto" value="' . $this->__get('goto')
  648. . '" />'
  649. . $input_for_real_end
  650. . '<input type="submit" name="navig"'
  651. . ' class="ajax" '
  652. . 'value="' . $caption_output . '" ' . $title_output . $onclick . ' />'
  653. . '</form>'
  654. . '</td>';
  655. } // end function _getTableNavigationButton()
  656. /**
  657. * Possibly return a page selector for table navigation
  658. *
  659. * @param string $table_navigation_html the current navigation HTML
  660. *
  661. * @return array ($table_navigation_html, $nbTotalPage)
  662. *
  663. * @access private
  664. *
  665. */
  666. private function _getHtmlPageSelector($table_navigation_html)
  667. {
  668. $pageNow = @floor(
  669. $_SESSION['tmpval']['pos']
  670. / $_SESSION['tmpval']['max_rows']
  671. ) + 1;
  672. $nbTotalPage = @ceil(
  673. $this->__get('unlim_num_rows')
  674. / $_SESSION['tmpval']['max_rows']
  675. );
  676. if ($nbTotalPage > 1) {
  677. $table_navigation_html .= '<td>';
  678. $_url_params = array(
  679. 'db' => $this->__get('db'),
  680. 'table' => $this->__get('table'),
  681. 'sql_query' => $this->__get('sql_query'),
  682. 'goto' => $this->__get('goto'),
  683. 'is_browse_distinct' => $this->__get('is_browse_distinct'),
  684. );
  685. //<form> to keep the form alignment of button < and <<
  686. // and also to know what to execute when the selector changes
  687. $table_navigation_html .= '<form action="sql.php'
  688. . PMA_URL_getCommon($_url_params)
  689. . '" method="post">';
  690. $table_navigation_html .= PMA_Util::pageselector(
  691. 'pos',
  692. $_SESSION['tmpval']['max_rows'],
  693. $pageNow, $nbTotalPage, 200, 5, 5, 20, 10
  694. );
  695. $table_navigation_html .= '</form>'
  696. . '</td>';
  697. }
  698. return array($table_navigation_html, $nbTotalPage);
  699. }
  700. /**
  701. * Get a navigation bar to browse among the results of a SQL query
  702. *
  703. * @param integer $pos_next the offset for the "next" page
  704. * @param integer $pos_prev the offset for the "previous" page
  705. * @param boolean $is_innodb whether its InnoDB or not
  706. *
  707. * @return string html content
  708. *
  709. * @access private
  710. *
  711. * @see _getTable()
  712. */
  713. private function _getTableNavigation(
  714. $pos_next, $pos_prev, $is_innodb
  715. ) {
  716. $table_navigation_html = '';
  717. // here, using htmlentities() would cause problems if the query
  718. // contains accented characters
  719. $html_sql_query = htmlspecialchars($this->__get('sql_query'));
  720. // Navigation bar
  721. $table_navigation_html .= '<table class="navigation nospacing nopadding print_ignore">'
  722. . '<tr>'
  723. . '<td class="navigation_separator"></td>';
  724. // Move to the beginning or to the previous page
  725. if ($_SESSION['tmpval']['pos']
  726. && ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS)
  727. ) {
  728. $table_navigation_html
  729. .= $this->_getMoveBackwardButtonsForTableNavigation(
  730. $html_sql_query, $pos_prev
  731. );
  732. } // end move back
  733. $nbTotalPage = 1;
  734. //page redirection
  735. // (unless we are showing all records)
  736. if ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS) {
  737. list(
  738. $table_navigation_html,
  739. $nbTotalPage
  740. ) = $this->_getHtmlPageSelector($table_navigation_html);
  741. }
  742. $showing_all = false;
  743. if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
  744. $showing_all = true;
  745. }
  746. // Move to the next page or to the last one
  747. $endpos = $_SESSION['tmpval']['pos']
  748. + $_SESSION['tmpval']['max_rows'];
  749. if (($endpos < $this->__get('unlim_num_rows'))
  750. && ($this->__get('num_rows') >= $_SESSION['tmpval']['max_rows'])
  751. && ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS)
  752. ) {
  753. $table_navigation_html
  754. .= $this->_getMoveForwardButtonsForTableNavigation(
  755. $html_sql_query, $pos_next, $is_innodb
  756. );
  757. } // end move toward
  758. // show separator if pagination happen
  759. if ($nbTotalPage > 1) {
  760. $table_navigation_html
  761. .= '<td><div class="navigation_separator">|</div></td>';
  762. }
  763. // Display the "Show all" button if allowed
  764. if ($GLOBALS['cfg']['ShowAll'] || ($this->__get('unlim_num_rows') <= 500) ) {
  765. $table_navigation_html .= $this->_getShowAllCheckboxForTableNavigation(
  766. $showing_all, $html_sql_query
  767. );
  768. $table_navigation_html
  769. .= '<td><div class="navigation_separator">|</div></td>';
  770. } // end show all
  771. $table_navigation_html .= '<td>'
  772. . '<div class="save_edited hide">'
  773. . '<input type="submit" value="' . __('Save edited data') . '" />'
  774. . '<div class="navigation_separator">|</div>'
  775. . '</div>'
  776. . '</td>'
  777. . '<td>'
  778. . '<div class="restore_column hide">'
  779. . '<input type="submit" value="' . __('Restore column order') . '" />'
  780. . '<div class="navigation_separator">|</div>'
  781. . '</div>'
  782. . '</td>';
  783. // if displaying a VIEW, $unlim_num_rows could be zero because
  784. // of $cfg['MaxExactCountViews']; in this case, avoid passing
  785. // the 5th parameter to checkFormElementInRange()
  786. // (this means we can't validate the upper limit
  787. $table_navigation_html .= '<td class="navigation_goto">';
  788. $table_navigation_html .= '<form action="sql.php" method="post" '
  789. . 'onsubmit="return '
  790. . '(checkFormElementInRange('
  791. . 'this, '
  792. . '\'session_max_rows\', '
  793. . '\''
  794. . str_replace('\'', '\\\'', __('%d is not valid row number.'))
  795. . '\', '
  796. . '1)'
  797. . ' &amp;&amp; '
  798. . 'checkFormElementInRange('
  799. . 'this, '
  800. . '\'pos\', '
  801. . '\''
  802. . str_replace('\'', '\\\'', __('%d is not valid row number.'))
  803. . '\', '
  804. . '0'
  805. . (($this->__get('unlim_num_rows') > 0)
  806. ? ', ' . ($this->__get('unlim_num_rows') - 1)
  807. : ''
  808. )
  809. . ')'
  810. . ')'
  811. . '">';
  812. $table_navigation_html .= PMA_URL_getHiddenInputs(
  813. $this->__get('db'), $this->__get('table')
  814. );
  815. $table_navigation_html .= $this->_getAdditionalFieldsForTableNavigation(
  816. $html_sql_query
  817. );
  818. $table_navigation_html .= '</form>'
  819. . '</td>'
  820. . '<td class="navigation_separator"></td>'
  821. . '<td>'
  822. . '<span>' . __('Filter rows') . ':</span>'
  823. . '<input type="text" class="filter_rows"'
  824. . ' placeholder="' . __('Search this table') . '"'
  825. . ' data-for="' . $this->__get('unique_id') . '" />'
  826. . '</td>'
  827. . '<td class="navigation_separator"></td>'
  828. . '</tr>'
  829. . '</table>';
  830. return $table_navigation_html;
  831. } // end of the '_getTableNavigation()' function
  832. /**
  833. * Prepare move backward buttons - previous and first
  834. *
  835. * @param string $html_sql_query the sql encoded by html special characters
  836. * @param integer $pos_prev the offset for the "previous" page
  837. *
  838. * @return string html content
  839. *
  840. * @access private
  841. *
  842. * @see _getTableNavigation()
  843. */
  844. private function _getMoveBackwardButtonsForTableNavigation(
  845. $html_sql_query, $pos_prev
  846. ) {
  847. return $this->_getTableNavigationButton(
  848. '&lt;&lt;', _pgettext('First page', 'Begin'), 0, $html_sql_query, true
  849. )
  850. . $this->_getTableNavigationButton(
  851. '&lt;', _pgettext('Previous page', 'Previous'), $pos_prev,
  852. $html_sql_query, true
  853. );
  854. } // end of the '_getMoveBackwardButtonsForTableNavigation()' function
  855. /**
  856. * Prepare Show All checkbox for table navigation
  857. *
  858. * @param bool $showing_all whether all rows are shown currently
  859. * @param string $html_sql_query the sql encoded by html special characters
  860. *
  861. * @return string html content
  862. *
  863. * @access private
  864. *
  865. * @see _getTableNavigation()
  866. */
  867. private function _getShowAllCheckboxForTableNavigation(
  868. $showing_all, $html_sql_query
  869. ) {
  870. return "\n"
  871. . '<td>'
  872. . '<form action="sql.php" method="post">'
  873. . PMA_URL_getHiddenInputs(
  874. $this->__get('db'), $this->__get('table')
  875. )
  876. . '<input type="hidden" name="sql_query" value="'
  877. . $html_sql_query . '" />'
  878. . '<input type="hidden" name="pos" value="0" />'
  879. . '<input type="hidden" name="is_browse_distinct" value="'
  880. . $this->__get('is_browse_distinct') . '" />'
  881. . '<input type="hidden" name="session_max_rows" value="'
  882. . (! $showing_all ? 'all' : $GLOBALS['cfg']['MaxRows']) . '" />'
  883. . '<input type="hidden" name="goto" value="' . $this->__get('goto')
  884. . '" />'
  885. . '<input type="checkbox" name="navig"'
  886. . ' id="showAll_' . $this->__get('unique_id') . '" class="showAllRows"'
  887. . (! $showing_all ? '' : ' checked="checked"') . ' value="all" />'
  888. . '<label for="showAll_' . $this->__get('unique_id') . '">'
  889. . __('Show all') . '</label>'
  890. . '</form>'
  891. . '</td>';
  892. } // end of the '_getShowAllButtonForTableNavigation()' function
  893. /**
  894. * Prepare move forward buttons - next and last
  895. *
  896. * @param string $html_sql_query the sql encoded by htmlspecialchars()
  897. * @param integer $pos_next the offset for the "next" page
  898. * @param boolean $is_innodb whether it's InnoDB or not
  899. *
  900. * @return string $buttons_html html content
  901. *
  902. * @access private
  903. *
  904. * @see _getTableNavigation()
  905. */
  906. private function _getMoveForwardButtonsForTableNavigation(
  907. $html_sql_query, $pos_next, $is_innodb
  908. ) {
  909. // display the Next button
  910. $buttons_html = $this->_getTableNavigationButton(
  911. '&gt;',
  912. _pgettext('Next page', 'Next'),
  913. $pos_next,
  914. $html_sql_query,
  915. false
  916. );
  917. // prepare some options for the End button
  918. if ($is_innodb
  919. && $this->__get('unlim_num_rows') > $GLOBALS['cfg']['MaxExactCount']
  920. ) {
  921. $input_for_real_end = '<input id="real_end_input" type="hidden" '
  922. . 'name="find_real_end" value="1" />';
  923. // no backquote around this message
  924. $onclick = '';
  925. } else {
  926. $input_for_real_end = $onclick = '';
  927. }
  928. $maxRows = $_SESSION['tmpval']['max_rows'];
  929. $onsubmit = 'onsubmit="return '
  930. . ($_SESSION['tmpval']['pos']
  931. + $maxRows
  932. < $this->__get('unlim_num_rows')
  933. && $this->__get('num_rows') >= $maxRows)
  934. ? 'true'
  935. : 'false' . '"';
  936. // display the End button
  937. $buttons_html .= $this->_getTableNavigationButton(
  938. '&gt;&gt;',
  939. _pgettext('Last page', 'End'),
  940. @((ceil(
  941. $this->__get('unlim_num_rows')
  942. / $_SESSION['tmpval']['max_rows']
  943. )- 1) * $maxRows),
  944. $html_sql_query, false, $onsubmit, $input_for_real_end, $onclick
  945. );
  946. return $buttons_html;
  947. } // end of the '_getMoveForwardButtonsForTableNavigation()' function
  948. /**
  949. * Prepare fields for table navigation
  950. * Number of rows
  951. *
  952. * @param string $html_sql_query the sql encoded by htmlspecialchars()
  953. *
  954. * @return string $additional_fields_html html content
  955. *
  956. * @access private
  957. *
  958. * @see _getTableNavigation()
  959. */
  960. private function _getAdditionalFieldsForTableNavigation(
  961. $html_sql_query
  962. ) {
  963. $additional_fields_html = '';
  964. $additional_fields_html .= '<input type="hidden" name="sql_query" '
  965. . 'value="' . $html_sql_query . '" />'
  966. . '<input type="hidden" name="goto" value="' . $this->__get('goto')
  967. . '" />'
  968. . '<input type="hidden" name="pos" size="3" value="'
  969. // Do not change the position when changing the number of rows
  970. . $_SESSION['tmpval']['pos'] . '" />'
  971. . '<input type="hidden" name="is_browse_distinct" value="'
  972. . $this->__get('is_browse_distinct') . '" />' ;
  973. $numberOfRowsPlaceholder = null;
  974. if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
  975. $numberOfRowsPlaceholder = __('All');
  976. }
  977. $numberOfRowsChoices = array(
  978. '25' => 25,
  979. '50' => 50,
  980. '100' => 100,
  981. '250' => 250,
  982. '500' => 500
  983. );
  984. $additional_fields_html .= __('Number of rows:') . ' ';
  985. $additional_fields_html .= PMA_Util::getDropdown(
  986. 'session_max_rows', $numberOfRowsChoices,
  987. $_SESSION['tmpval']['max_rows'], '',
  988. 'autosubmit', $numberOfRowsPlaceholder
  989. );
  990. return $additional_fields_html;
  991. } // end of the '_getAdditionalFieldsForTableNavigation()' function
  992. /**
  993. * Get the headers of the results table, for all of the columns
  994. *
  995. * @param array $displayParts which elements to display
  996. * @param array $analyzed_sql_results analyzed sql results
  997. * @param array $sort_expression sort expression
  998. * @param string $sort_expression_nodirection sort expression
  999. * without direction
  1000. * @param string $sort_direction sort direction
  1001. * @param boolean $is_limited_display with limited operations
  1002. * or not
  1003. * @param string $unsorted_sql_query query without the sort part
  1004. *
  1005. * @return string html content
  1006. *
  1007. * @access private
  1008. *
  1009. * @see getTableHeaders()
  1010. */
  1011. private function _getTableHeadersForColumns(
  1012. $displayParts, $analyzed_sql_results, $sort_expression,
  1013. $sort_expression_nodirection, $sort_direction, $is_limited_display,
  1014. $unsorted_sql_query
  1015. ) {
  1016. $html = '';
  1017. // required to generate sort links that will remember whether the
  1018. // "Show all" button has been clicked
  1019. $sql_md5 = md5($this->__get('sql_query'));
  1020. $session_max_rows = $is_limited_display
  1021. ? 0
  1022. : $_SESSION['tmpval']['query'][$sql_md5]['max_rows'];
  1023. // Following variable are needed for use in isset/empty or
  1024. // use with array indexes/safe use in the for loop
  1025. $highlight_columns = $this->__get('highlight_columns');
  1026. $fields_meta = $this->__get('fields_meta');
  1027. // Prepare Display column comments if enabled
  1028. // ($GLOBALS['cfg']['ShowBrowseComments']).
  1029. $comments_map = $this->_getTableCommentsArray($analyzed_sql_results);
  1030. list($col_order, $col_visib) = $this->_getColumnParams($analyzed_sql_results);
  1031. // optimize: avoid calling a method on each iteration
  1032. $number_of_columns = $this->__get('fields_cnt');
  1033. for ($j = 0; $j < $number_of_columns; $j++) {
  1034. // assign $i with the appropriate column order
  1035. $i = $col_order ? $col_order[$j] : $j;
  1036. // See if this column should get highlight because it's used in the
  1037. // where-query.
  1038. $condition_field = (isset($highlight_columns[$fields_meta[$i]->name])
  1039. || isset(
  1040. $highlight_columns[PMA_Util::backquote($fields_meta[$i]->name)])
  1041. )
  1042. ? true
  1043. : false;
  1044. // Prepare comment-HTML-wrappers for each row, if defined/enabled.
  1045. $comments = $this->_getCommentForRow($comments_map, $fields_meta[$i]);
  1046. $display_params = $this->__get('display_params');
  1047. if (($displayParts['sort_lnk'] == '1') && ! $is_limited_display) {
  1048. list($order_link, $sorted_header_html)
  1049. = $this->_getOrderLinkAndSortedHeaderHtml(
  1050. $fields_meta[$i], $sort_expression,
  1051. $sort_expression_nodirection, $i, $unsorted_sql_query,
  1052. $session_max_rows, $comments,
  1053. $sort_direction, $col_visib,
  1054. $col_visib[$j]
  1055. );
  1056. $html .= $sorted_header_html;
  1057. $display_params['desc'][] = ' <th '
  1058. . 'class="draggable'
  1059. . ($condition_field ? ' condition' : '')
  1060. . '" data-column="' . htmlspecialchars($fields_meta[$i]->name)
  1061. . '">' . "\n" . $order_link . $comments . ' </th>' . "\n";
  1062. } else {
  1063. // Results can't be sorted
  1064. $html
  1065. .= $this->_getDraggableClassForNonSortableColumns(
  1066. $col_visib, $col_visib[$j], $condition_field,
  1067. $fields_meta[$i], $comments
  1068. );
  1069. $display_params['desc'][] = ' <th '
  1070. . 'class="draggable'
  1071. . ($condition_field ? ' condition"' : '')
  1072. . '" data-column="' . htmlspecialchars($fields_meta[$i]->name)
  1073. . '">' . ' '
  1074. . htmlspecialchars($fields_meta[$i]->name)
  1075. . $comments . ' </th>';
  1076. } // end else
  1077. $this->__set('display_params', $display_params);
  1078. } // end for
  1079. return $html;
  1080. }
  1081. /**
  1082. * Get the headers of the results table
  1083. *
  1084. * @param array &$displayParts which elements to display
  1085. * @param array $analyzed_sql_results analyzed sql results
  1086. * @param array $sort_expression sort expression
  1087. * @param string $sort_expression_nodirection sort expression
  1088. * without direction
  1089. * @param string $sort_direction sort direction
  1090. * @param boolean $is_limited_display with limited operations
  1091. * or not
  1092. *
  1093. * @return string html content
  1094. *
  1095. * @access private
  1096. *
  1097. * @see getTable()
  1098. */
  1099. private function _getTableHeaders(
  1100. &$displayParts, $analyzed_sql_results, $sort_expression = array(),
  1101. $sort_expression_nodirection = '', $sort_direction = '',
  1102. $is_limited_display = false
  1103. ) {
  1104. $table_headers_html = '';
  1105. // Needed for use in isset/empty or
  1106. // use with array indexes/safe use in foreach
  1107. $printview = $this->__get('printview');
  1108. $display_params = $this->__get('display_params');
  1109. // can the result be sorted?
  1110. if ($displayParts['sort_lnk'] == '1') {
  1111. // At this point, $sort_expression is an array but we only verify
  1112. // the first element in case we could find that the table is
  1113. // sorted by one of the choices listed in the
  1114. // "Sort by key" drop-down
  1115. list($unsorted_sql_query, $drop_down_html)
  1116. = $this->_getUnsortedSqlAndSortByKeyDropDown(
  1117. $analyzed_sql_results, $sort_expression[0]
  1118. );
  1119. $table_headers_html .= $drop_down_html;
  1120. } else {
  1121. $unsorted_sql_query = '';
  1122. }
  1123. // Output data needed for grid editing
  1124. $table_headers_html .= '<input class="save_cells_at_once" type="hidden"'
  1125. . ' value="' . $GLOBALS['cfg']['SaveCellsAtOnce'] . '" />'
  1126. . '<div class="common_hidden_inputs">'
  1127. . PMA_URL_getHiddenInputs(
  1128. $this->__get('db'), $this->__get('table')
  1129. )
  1130. . '</div>';
  1131. // Output data needed for column reordering and show/hide column
  1132. if ($this->_isSelect($analyzed_sql_results)) {
  1133. $table_headers_html .= $this->_getDataForResettingColumnOrder();
  1134. }
  1135. $display_params['emptypre'] = 0;
  1136. $display_params['emptyafter'] = 0;
  1137. $display_params['textbtn'] = '';
  1138. $full_or_partial_text_link = null;
  1139. $this->__set('display_params', $display_params);
  1140. // Display options (if we are not in print view)
  1141. if (! (isset($printview) && ($printview == '1')) && ! $is_limited_display) {
  1142. $table_headers_html .= $this->_getOptionsBlock();
  1143. // prepare full/partial text button or link
  1144. $full_or_partial_text_link = $this->_getFullOrPartialTextButtonOrLink();
  1145. }
  1146. // Start of form for multi-rows edit/delete/export
  1147. $table_headers_html .= $this->_getFormForMultiRowOperations(
  1148. $displayParts['del_lnk']
  1149. );
  1150. // 1. Set $colspan and generate html with full/partial
  1151. // text button or link
  1152. list($colspan, $button_html)
  1153. = $this->_getFieldVisibilityParams(
  1154. $displayParts, $full_or_partial_text_link
  1155. );
  1156. $table_headers_html .= $button_html;
  1157. // 2. Displays the fields' name
  1158. // 2.0 If sorting links should be used, checks if the query is a "JOIN"
  1159. // statement (see 2.1.3)
  1160. // See if we have to highlight any header fields of a WHERE query.
  1161. // Uses SQL-Parser results.
  1162. $this->_setHighlightedColumnGlobalField($analyzed_sql_results);
  1163. // Get the headers for all of the columns
  1164. $table_headers_html .= $this->_getTableHeadersForColumns(
  1165. $displayParts, $analyzed_sql_results, $sort_expression,
  1166. $sort_expression_nodirection, $sort_direction,
  1167. $is_limited_display, $unsorted_sql_query
  1168. );
  1169. // Display column at rightside - checkboxes or empty column
  1170. if (! $printview) {
  1171. $table_headers_html .= $this->_getColumnAtRightSide(
  1172. $displayParts, $full_or_partial_text_link, $colspan
  1173. );
  1174. }
  1175. $table_headers_html .= '</tr>' . '</thead>';
  1176. return $table_headers_html;
  1177. } // end of the '_getTableHeaders()' function
  1178. /**
  1179. * Prepare unsorted sql query and sort by key drop down
  1180. *
  1181. * @param array $analyzed_sql_results analyzed sql results
  1182. * @param string $sort_expression sort expression
  1183. *
  1184. * @return array two element array - $unsorted_sql_query, $drop_down_html
  1185. *
  1186. * @access private
  1187. *
  1188. * @see _getTableHeaders()
  1189. */
  1190. private function _getUnsortedSqlAndSortByKeyDropDown(
  1191. $analyzed_sql_results, $sort_expression
  1192. ) {
  1193. $drop_down_html = '';
  1194. $unsorted_sql_query = SqlParser\Utils\Query::replaceClause(
  1195. $analyzed_sql_results['statement'],
  1196. $analyzed_sql_results['parser']->list,
  1197. 'ORDER BY',
  1198. ''
  1199. );
  1200. // Data is sorted by indexes only if it there is only one table.
  1201. if ($this->_isSelect($analyzed_sql_results)) {
  1202. // grab indexes data:
  1203. $indexes = PMA_Index::getFromTable(
  1204. $this->__get('table'),
  1205. $this->__get('db')
  1206. );
  1207. // do we have any index?
  1208. if (! empty($indexes)) {
  1209. $drop_down_html = $this->_getSortByKeyDropDown(
  1210. $indexes, $sort_expression,
  1211. $unsorted_sql_query
  1212. );
  1213. }
  1214. }
  1215. return array($unsorted_sql_query, $drop_down_html);
  1216. } // end of the '_getUnsortedSqlAndSortByKeyDropDown()' function
  1217. /**
  1218. * Prepare sort by key dropdown - html code segment
  1219. *
  1220. * @param PMA_Index[] $indexes the indexes of the table for sort
  1221. * criteria
  1222. * @param string $sort_expression the sort expression
  1223. * @param string $unsorted_sql_query the unsorted sql query
  1224. *
  1225. * @return string $drop_down_html html content
  1226. *
  1227. * @access private
  1228. *
  1229. * @see _getTableHeaders()
  1230. */
  1231. private function _getSortByKeyDropDown(
  1232. $indexes, $sort_expression, $unsorted_sql_query
  1233. ) {
  1234. $drop_down_html = '';
  1235. $drop_down_html .= '<form action="sql.php" method="post" class="print_ignore">' . "\n"
  1236. . PMA_URL_getHiddenInputs(
  1237. $this->__get('db'), $this->__get('table')
  1238. )
  1239. // to avoid calling PMA_handleSortOrder() later
  1240. . PMA_getHiddenFields(array('sort_by_key' => '1'))
  1241. . __('Sort by key')
  1242. . ': <select name="sql_query" class="autosubmit">' . "\n";
  1243. $used_index = false;
  1244. $local_order = (isset($sort_expression) ? $sort_expression : '');
  1245. foreach ($indexes as $index) {
  1246. $asc_sort = '`'
  1247. . implode('` ASC, `', array_keys($index->getColumns()))
  1248. . '` ASC';
  1249. $desc_sort = '`'
  1250. . implode('` DESC, `', array_keys($index->getColumns()))
  1251. . '` DESC';
  1252. $used_index = $used_index
  1253. || ($local_order == $asc_sort)
  1254. || ($local_order == $desc_sort);
  1255. if (preg_match(
  1256. '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|'
  1257. . 'FOR UPDATE|LOCK IN SHARE MODE))@is',
  1258. $unsorted_sql_query, $my_reg
  1259. )) {
  1260. $unsorted_sql_query_first_part = $my_reg[1];
  1261. $unsorted_sql_query_second_part = $my_reg[2];
  1262. } else {
  1263. $unsorted_sql_query_first_part = $unsorted_sql_query;
  1264. $unsorted_sql_query_second_part = '';
  1265. }
  1266. $drop_down_html .= '<option value="'
  1267. . htmlspecialchars(
  1268. $unsorted_sql_query_first_part . "\n"
  1269. . ' ORDER BY ' . $asc_sort
  1270. . $unsorted_sql_query_second_part
  1271. )
  1272. . '"' . ($local_order == $asc_sort
  1273. ? ' selected="selected"'
  1274. : '')
  1275. . '>' . htmlspecialchars($index->getName()) . ' ('
  1276. . __('Ascending') . ')</option>';
  1277. $drop_down_html .= '<option value="'
  1278. . htmlspecialchars(
  1279. $unsorted_sql_query_first_part . "\n"
  1280. . ' ORDER BY ' . $desc_sort
  1281. . $unsorted_sql_query_second_part
  1282. )
  1283. . '"' . ($local_order == $desc_sort
  1284. ? ' selected="selected"'
  1285. : '')
  1286. . '>' . htmlspecialchars($index->getName()) . ' ('
  1287. . __('Descending') . ')</option>';
  1288. }
  1289. $drop_down_html .= '<option value="' . htmlspecialchars($unsorted_sql_query)
  1290. . '"' . ($used_index ? '' : ' selected="selected"') . '>' . __('None')
  1291. . '</option>'
  1292. . '</select>' . "\n"
  1293. . '</form>' . "\n";
  1294. return $drop_down_html;
  1295. } // end of the '_getSortByKeyDropDown()' function
  1296. /**
  1297. * Set column span, row span and prepare html with full/partial
  1298. * text button or link
  1299. *
  1300. * @param array &$displayParts which elements to display
  1301. * @param string $full_or_partial_text_link full/partial link or text button
  1302. *
  1303. * @return array 2 element array - $colspan, $button_html
  1304. *
  1305. * @access private
  1306. *
  1307. * @see _getTableHeaders()
  1308. */
  1309. private function _getFieldVisibilityParams(
  1310. &$displayParts, $full_or_partial_text_link
  1311. ) {
  1312. $button_html = '';
  1313. $display_params = $this->__get('display_params');
  1314. // 1. Displays the full/partial text button (part 1)...
  1315. $button_html .= '<thead><tr>' . "\n";
  1316. $colspan = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  1317. && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE))
  1318. ? ' colspan="4"'
  1319. : '';
  1320. // ... before the result table
  1321. if ((($displayParts['edit_lnk'] == self::NO_EDIT_OR_DELETE)
  1322. && ($displayParts['del_lnk'] == self::NO_EDIT_OR_DELETE))
  1323. && ($displayParts['text_btn'] == '1')
  1324. ) {
  1325. $display_params['emptypre']
  1326. = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  1327. && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0;
  1328. } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
  1329. || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
  1330. && ($displayParts['text_btn'] == '1')
  1331. ) {
  1332. // ... at the left column of the result table header if possible
  1333. // and required
  1334. $display_params['emptypre']
  1335. = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  1336. && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0;
  1337. $button_html .= '<th class="column_action print_ignore" ' . $colspan . '>'
  1338. . $full_or_partial_text_link . '</th>';
  1339. } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
  1340. || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
  1341. && (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  1342. || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE))
  1343. ) {
  1344. // ... elseif no button, displays empty(ies) col(s) if required
  1345. $display_params['emptypre']
  1346. = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  1347. && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0;
  1348. $button_html .= '<td ' . $colspan . '></td>';
  1349. } elseif (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE)) {
  1350. // ... elseif display an empty column if the actions links are
  1351. // disabled to match the rest of the table
  1352. $button_html .= '<th class="column_action"></th>';
  1353. }
  1354. $this->__set('display_params', $display_params);
  1355. return array($colspan, $button_html);
  1356. } // end of the '_getFieldVisibilityParams()' function
  1357. /**
  1358. * Get table comments as array
  1359. *
  1360. * @param array $analyzed_sql_results analyzed sql results
  1361. *
  1362. * @return array $comments_map table comments
  1363. *
  1364. * @access private
  1365. *
  1366. * @see _getTableHeaders()
  1367. */
  1368. private function _getTableCommentsArray($analyzed_sql_results)
  1369. {
  1370. if ((!$GLOBALS['cfg']['ShowBrowseComments'])
  1371. || (empty($analyzed_sql_results['statement']->from))
  1372. ) {
  1373. return array();
  1374. }
  1375. $ret = array();
  1376. foreach ($analyzed_sql_results['statement']->from as $field) {
  1377. if (empty($field->table)) {
  1378. continue;
  1379. }
  1380. $ret[$field->table] = PMA_getComments(
  1381. empty($field->database) ? $this->__get('db') : $field->database,
  1382. $field->table
  1383. );
  1384. }
  1385. return $ret;
  1386. } // end of the '_getTableCommentsArray()' function
  1387. /**
  1388. * Set global array for store highlighted header fields
  1389. *
  1390. * @param array $analyzed_sql_results analyzed sql results
  1391. *
  1392. * @return void
  1393. *
  1394. * @access private
  1395. *
  1396. * @see _getTableHeaders()
  1397. */
  1398. private function _setHighlightedColumnGlobalField($analyzed_sql_results)
  1399. {
  1400. $highlight_columns = array();
  1401. if (!empty($analyzed_sql_results['statement']->where)) {
  1402. foreach ($analyzed_sql_results['statement']->where as $expr) {
  1403. foreach ($expr->identifiers as $identifier) {
  1404. $highlight_columns[$identifier] = 'true';
  1405. }
  1406. }
  1407. }
  1408. $this->__set('highlight_columns', $highlight_columns);
  1409. } // end of the '_setHighlightedColumnGlobalField()' function
  1410. /**
  1411. * Prepare data for column restoring and show/hide
  1412. *
  1413. * @return string $data_html html content
  1414. *
  1415. * @access private
  1416. *
  1417. * @see _getTableHeaders()
  1418. */
  1419. private function _getDataForResettingColumnOrder()
  1420. {
  1421. $data_html = '';
  1422. // generate the column order, if it is set
  1423. $pmatable = new PMA_Table($this->__get('table'), $this->__get('db'));
  1424. $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER);
  1425. if ($col_order) {
  1426. $data_html .= '<input class="col_order" type="hidden" value="'
  1427. . implode(',', $col_order) . '" />';
  1428. }
  1429. $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB);
  1430. if ($col_visib) {
  1431. $data_html .= '<input class="col_visib" type="hidden" value="'
  1432. . implode(',', $col_visib) . '" />';
  1433. }
  1434. // generate table create time
  1435. $table = new PMA_Table($this->__get('table'), $this->__get('db'));
  1436. if (! $table->isView()) {
  1437. $data_html .= '<input class="table_create_time" type="hidden" value="'
  1438. . $GLOBALS['dbi']->getTable(
  1439. $this->__get('db'), $this->__get('table')
  1440. )->getStatusInfo('Create_time')
  1441. . '" />';
  1442. }
  1443. return $data_html;
  1444. } // end of the '_getDataForResettingColumnOrder()' function
  1445. /**
  1446. * Prepare option fields block
  1447. *
  1448. * @return string $options_html html content
  1449. *
  1450. * @access private
  1451. *
  1452. * @see _getTableHeaders()
  1453. */
  1454. private function _getOptionsBlock()
  1455. {
  1456. $options_html = '';
  1457. $options_html .= '<form method="post" action="sql.php" '
  1458. . 'name="displayOptionsForm"';
  1459. $options_html .= ' class="ajax print_ignore" ';
  1460. $options_html .= '>';
  1461. $url_params = array(
  1462. 'db' => $this->__get('db'),
  1463. 'table' => $this->__get('table'),
  1464. 'sql_query' => $this->__get('sql_query'),
  1465. 'goto' => $this->__get('goto'),
  1466. 'display_options_form' => 1
  1467. );
  1468. $options_html .= PMA_URL_getHiddenInputs($url_params)
  1469. . '<br />'
  1470. . PMA_Util::getDivForSliderEffect(
  1471. '', __('Options')
  1472. )
  1473. . '<fieldset>';
  1474. $options_html .= '<div class="formelement">';
  1475. $choices = array(
  1476. 'P' => __('Partial texts'),
  1477. 'F' => __('Full texts')
  1478. );
  1479. // pftext means "partial or full texts" (done to reduce line lengths)
  1480. $options_html .= PMA_Util::getRadioFields(
  1481. 'pftext', $choices,
  1482. $_SESSION['tmpval']['pftext'],
  1483. true, true, '', 'pftext_' . $this->__get('unique_id')
  1484. )
  1485. . '</div>';
  1486. if ($GLOBALS['cfgRelation']['relwork']
  1487. && $GLOBALS['cfgRelation']['displaywork']
  1488. ) {
  1489. $options_html .= '<div class="formelement">';
  1490. $choices = array(
  1491. 'K' => __('Relational key'),
  1492. 'D' => __('Display column for relations')
  1493. );
  1494. $options_html .= PMA_Util::getRadioFields(
  1495. 'relational_display', $choices,
  1496. $_SESSION['tmpval']['relational_display'],
  1497. true, true, '', 'relational_display_' . $this->__get('unique_id')
  1498. )
  1499. . '</div>';
  1500. }
  1501. $options_html .= '<div class="formelement">'
  1502. . PMA_Util::getCheckbox(
  1503. 'display_binary', __('Show binary contents'),
  1504. ! empty($_SESSION['tmpval']['display_binary']), false,
  1505. 'display_binary_' . $this->__get('unique_id')
  1506. )
  1507. . '<br />'
  1508. . PMA_Util::getCheckbox(
  1509. 'display_blob', __('Show BLOB contents'),
  1510. ! empty($_SESSION['tmpval']['display_blob']), false,
  1511. 'display_blob_' . $this->__get('unique_id')
  1512. )
  1513. . '</div>';
  1514. // I would have preferred to name this "display_transformation".
  1515. // This is the only way I found to be able to keep this setting sticky
  1516. // per SQL query, and at the same time have a default that displays
  1517. // the transformations.
  1518. $options_html .= '<div class="formelement">'
  1519. . PMA_Util::getCheckbox(
  1520. 'hide_transformation', __('Hide browser transformation'),
  1521. ! empty($_SESSION['tmpval']['hide_transformation']), false,
  1522. 'hide_transformation_' . $this->__get('unique_id')
  1523. )
  1524. . '</div>';
  1525. if (! PMA_DRIZZLE) {
  1526. $options_html .= '<div class="formelement">';
  1527. $choices = array(
  1528. 'GEOM' => __('Geometry'),
  1529. 'WKT' => __('Well Known Text'),
  1530. 'WKB' => __('Well Known Binary')
  1531. );
  1532. $options_html .= PMA_Util::getRadioFields(
  1533. 'geoOption', $choices,
  1534. $_SESSION['tmpval']['geoOption'],
  1535. true, true, '', 'geoOption_' . $this->__get('unique_id')
  1536. )
  1537. . '</div>';
  1538. }
  1539. $options_html .= '<div class="clearfloat"></div>'
  1540. . '</fieldset>';
  1541. $options_html .= '<fieldset class="tblFooters">'
  1542. . '<input type="submit" value="' . __('Go') . '" />'
  1543. . '</fieldset>'
  1544. . '</div>'
  1545. . '</form>';
  1546. return $options_html;
  1547. } // end of the '_getOptionsBlock()' function
  1548. /**
  1549. * Get full/partial text button or link
  1550. *
  1551. * @return string html content
  1552. *
  1553. * @access private
  1554. *
  1555. * @see _getTableHeaders()
  1556. */
  1557. private function _getFullOrPartialTextButtonOrLink()
  1558. {
  1559. $url_params_full_text = array(
  1560. 'db' => $this->__get('db'),
  1561. 'table' => $this->__get('table'),
  1562. 'sql_query' => $this->__get('sql_query'),
  1563. 'goto' => $this->__get('goto'),
  1564. 'full_text_button' => 1
  1565. );
  1566. if ($_SESSION['tmpval']['pftext'] == self::DISPLAY_FULL_TEXT) {
  1567. // currently in fulltext mode so show the opposite link
  1568. $tmp_image_file = $this->__get('pma_theme_image') . 's_partialtext.png';
  1569. $tmp_txt = __('Partial texts');
  1570. $url_params_full_text['pftext'] = self::DISPLAY_PARTIAL_TEXT;
  1571. } else {
  1572. $tmp_image_file = $this->__get('pma_theme_image') . 's_fulltext.png';
  1573. $tmp_txt = __('Full texts');
  1574. $url_params_full_text['pftext'] = self::DISPLAY_FULL_TEXT;
  1575. }
  1576. $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="'
  1577. . $tmp_txt . '" title="' . $tmp_txt . '" />';
  1578. $tmp_url = 'sql.php' . PMA_URL_getCommon($url_params_full_text);
  1579. return PMA_Util::linkOrButton(
  1580. $tmp_url, $tmp_image, array(), false
  1581. );
  1582. } // end of the '_getFullOrPartialTextButtonOrLink()' function
  1583. /**
  1584. * Prepare html form for multi row operations
  1585. *
  1586. * @param string $del_lnk the delete link of current row
  1587. *
  1588. * @return string $form_html html content
  1589. *
  1590. * @access private
  1591. *
  1592. * @see _getTableHeaders()
  1593. */
  1594. private function _getFormForMultiRowOperations($del_lnk)
  1595. {
  1596. $form_html = '';
  1597. if (($del_lnk == self::DELETE_ROW) || ($del_lnk == self::KILL_PROCESS)) {
  1598. $form_html .= '<form method="post" action="tbl_row_action.php" '
  1599. . 'name="resultsForm"'
  1600. . ' id="resultsForm_' . $this->__get('unique_id') . '"';
  1601. $form_html .= ' class="ajax" ';
  1602. $form_html .= '>'
  1603. . PMA_URL_getHiddenInputs(
  1604. $this->__get('db'), $this->__get('table'), 1
  1605. )
  1606. . '<input type="hidden" name="goto" value="sql.php" />';
  1607. }
  1608. $form_html .= '<table class="table_results data ajax"';
  1609. $form_html .= ' data-uniqueId="' . $this->__get('unique_id') . '"';
  1610. $form_html .= '>';
  1611. return $form_html;
  1612. } // end of the '_getFormForMultiRowOperations()' function
  1613. /**
  1614. * Get comment for row
  1615. *
  1616. * @param array $comments_map comments array
  1617. * @param array $fields_meta set of field properties
  1618. *
  1619. * @return string $comment html content
  1620. *
  1621. * @access private
  1622. *
  1623. * @see _getTableHeaders()
  1624. */
  1625. private function _getCommentForRow($comments_map, $fields_meta)
  1626. {
  1627. $comments = '';
  1628. if (isset($comments_map[$fields_meta->table])
  1629. && isset($comments_map[$fields_meta->table][$fields_meta->name])
  1630. ) {
  1631. $sanitized_comments = htmlspecialchars(
  1632. $comments_map[$fields_meta->table][$fields_meta->name]
  1633. );
  1634. $comments = '<span class="tblcomment" title="'
  1635. . $sanitized_comments . '">';
  1636. $limitChars = $GLOBALS['cfg']['LimitChars'];
  1637. if (/*overload*/mb_strlen($sanitized_comments) > $limitChars) {
  1638. $sanitized_comments = /*overload*/mb_substr(
  1639. $sanitized_comments, 0, $limitChars
  1640. ) . '…';
  1641. }
  1642. $comments .= $sanitized_comments;
  1643. $comments .= '</span>';
  1644. }
  1645. return $comments;
  1646. } // end of the '_getCommentForRow()' function
  1647. /**
  1648. * Prepare parameters and html for sorted table header fields
  1649. *
  1650. * @param array $fields_meta set of field properties
  1651. * @param array $sort_expression sort expression
  1652. * @param string $sort_expression_nodirection sort expression without direction
  1653. * @param integer $column_index the index of the column
  1654. * @param string $unsorted_sql_query the unsorted sql query
  1655. * @param integer $session_max_rows maximum rows resulted by sql
  1656. * @param string $comments comment for row
  1657. * @param string $sort_direction sort direction
  1658. * @param boolean $col_visib column is visible(false)
  1659. * array column isn't visible(string array)
  1660. * @param string $col_visib_j element of $col_visib array
  1661. *
  1662. * @return array 2 element array - $order_link, $sorted_header_html
  1663. *
  1664. * @access private
  1665. *
  1666. * @see _getTableHeaders()
  1667. */
  1668. private function _getOrderLinkAndSortedHeaderHtml(
  1669. $fields_meta, $sort_expression, $sort_expression_nodirection,
  1670. $column_index, $unsorted_sql_query, $session_max_rows,
  1671. $comments, $sort_direction, $col_visib, $col_visib_j
  1672. ) {
  1673. $sorted_header_html = '';
  1674. // Checks if the table name is required; it's the case
  1675. // for a query with a "JOIN" statement and if the column
  1676. // isn't aliased, or in queries like
  1677. // SELECT `1`.`master_field` , `2`.`master_field`
  1678. // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2`
  1679. $sort_tbl = (isset($fields_meta->table)
  1680. && /*overload*/mb_strlen($fields_meta->table)
  1681. && $fields_meta->orgname == $fields_meta->name)
  1682. ? PMA_Util::backquote(
  1683. $fields_meta->table
  1684. ) . '.'
  1685. : '';
  1686. $name_to_use_in_sort = $fields_meta->name;
  1687. // Generates the orderby clause part of the query which is part
  1688. // of URL
  1689. list($single_sort_order, $multi_sort_order, $order_img)
  1690. = $this->_getSingleAndMultiSortUrls(
  1691. $sort_expression, $sort_expression_nodirection, $sort_tbl,
  1692. $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index
  1693. );
  1694. if (preg_match(
  1695. '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|'
  1696. . 'LOCK IN SHARE MODE))@is',
  1697. $unsorted_sql_query, $regs3
  1698. )) {
  1699. $single_sorted_sql_query = $regs3[1] . $single_sort_order . $regs3[2];
  1700. $multi_sorted_sql_query = $regs3[1] . $multi_sort_order . $regs3[2];
  1701. } else {
  1702. $single_sorted_sql_query = $unsorted_sql_query . $single_sort_order;
  1703. $multi_sorted_sql_query = $unsorted_sql_query . $multi_sort_order;
  1704. }
  1705. $_single_url_params = array(
  1706. 'db' => $this->__get('db'),
  1707. 'table' => $this->__get('table'),
  1708. 'sql_query' => $single_sorted_sql_query,
  1709. 'session_max_rows' => $session_max_rows,
  1710. 'is_browse_distinct' => $this->__get('is_browse_distinct'),
  1711. );
  1712. $_multi_url_params = array(
  1713. 'db' => $this->__get('db'),
  1714. 'table' => $this->__get('table'),
  1715. 'sql_query' => $multi_sorted_sql_query,
  1716. 'session_max_rows' => $session_max_rows,
  1717. 'is_browse_distinct' => $this->__get('is_browse_distinct'),
  1718. );
  1719. $single_order_url = 'sql.php' . PMA_URL_getCommon($_single_url_params);
  1720. $multi_order_url = 'sql.php' . PMA_URL_getCommon($_multi_url_params);
  1721. // Displays the sorting URL
  1722. // enable sort order swapping for image
  1723. $order_link = $this->_getSortOrderLink(
  1724. $order_img, $column_index,
  1725. $fields_meta, $single_order_url, $multi_order_url
  1726. );
  1727. $sorted_header_html .= $this->_getDraggableClassForSortableColumns(
  1728. $col_visib, $col_visib_j,
  1729. $fields_meta, $order_link, $comments
  1730. );
  1731. return array($order_link, $sorted_header_html);
  1732. } // end of the '_getOrderLinkAndSortedHeaderHtml()' function
  1733. /**
  1734. * Prepare parameters and html for sorted table header fields
  1735. *
  1736. * @param array $sort_expression sort expression
  1737. * @param string $sort_expression_nodirection sort expression without direction
  1738. * @param string $sort_tbl The name of the table to which
  1739. * the current column belongs to
  1740. * @param string $name_to_use_in_sort The current column under
  1741. * consideration
  1742. * @param string $sort_direction sort direction
  1743. * @param array $fields_meta set of field properties
  1744. * @param integer $column_index The index number to current column
  1745. *
  1746. * @return array 3 element array - $single_sort_order, $sort_order, $order_img
  1747. *
  1748. * @access private
  1749. *
  1750. * @see _getOrderLinkAndSortedHeaderHtml()
  1751. */
  1752. private function _getSingleAndMultiSortUrls(
  1753. $sort_expression, $sort_expression_nodirection, $sort_tbl,
  1754. $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index
  1755. ) {
  1756. $sort_order = "";
  1757. // Check if the current column is in the order by clause
  1758. $is_in_sort = $this->_isInSorted(
  1759. $sort_expression, $sort_expression_nodirection,
  1760. $sort_tbl, $name_to_use_in_sort
  1761. );
  1762. $current_name = $name_to_use_in_sort;
  1763. if ($sort_expression_nodirection[0] == '' || !$is_in_sort) {
  1764. $special_index = $sort_expression_nodirection[0] == ''
  1765. ? 0
  1766. : count($sort_expression_nodirection);
  1767. $sort_expression_nodirection[$special_index]
  1768. = PMA_Util::backquote(
  1769. $current_name
  1770. );
  1771. $sort_direction[$special_index] = (preg_match(
  1772. '@time|date@i',
  1773. $fields_meta->type
  1774. )) ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR;
  1775. }
  1776. $sort_expression_nodirection = array_filter($sort_expression_nodirection);
  1777. $single_sort_order = null;
  1778. foreach ($sort_expression_nodirection as $index=>$expression) {
  1779. // check if this is the first clause,
  1780. // if it is then we have to add "order by"
  1781. $is_first_clause = ($index == 0);
  1782. $name_to_use_in_sort = $expression;
  1783. $sort_tbl_new = $sort_tbl;
  1784. // Test to detect if the column name is a standard name
  1785. // Standard name has the table name prefixed to the column name
  1786. if (/*overload*/mb_strpos($name_to_use_in_sort, '.') !== false) {
  1787. $matches = explode('.', $name_to_use_in_sort);
  1788. // Matches[0] has the table name
  1789. // Matches[1] has the column name
  1790. $name_to_use_in_sort = $matches[1];
  1791. $sort_tbl_new = $matches[0];
  1792. }
  1793. // $name_to_use_in_sort might contain a space due to
  1794. // formatting of function expressions like "COUNT(name )"
  1795. // so we remove the space in this situation
  1796. $name_to_use_in_sort = str_replace(' )', ')', $name_to_use_in_sort);
  1797. $name_to_use_in_sort = str_replace('``', '`', $name_to_use_in_sort);
  1798. $name_to_use_in_sort = trim($name_to_use_in_sort, '`');
  1799. // If this the first column name in the order by clause add
  1800. // order by clause to the column name
  1801. $query_head = $is_first_clause ? "\nORDER BY " : "";
  1802. // Again a check to see if the given column is a aggregate column
  1803. if (/*overload*/mb_strpos($name_to_use_in_sort, '(') !== false) {
  1804. $sort_order .= $query_head . $name_to_use_in_sort . ' ' ;
  1805. } else {
  1806. if (/*overload*/mb_strlen($sort_tbl_new) > 0) {
  1807. $sort_tbl_new .= ".";
  1808. }
  1809. $sort_order .= $query_head . $sort_tbl_new
  1810. . PMA_Util::backquote(
  1811. $name_to_use_in_sort
  1812. ) . ' ' ;
  1813. }
  1814. // For a special case where the code generates two dots between
  1815. // column name and table name.
  1816. $sort_order = preg_replace("/\.\./", ".", $sort_order);
  1817. // Incase this is the current column save $single_sort_order
  1818. if ($current_name == $name_to_use_in_sort) {
  1819. if (/*overload*/mb_strpos($current_name, '(') !== false) {
  1820. $single_sort_order = "\n" . 'ORDER BY ' . $current_name . ' ';
  1821. } else {
  1822. $single_sort_order = "\n" . 'ORDER BY ' . $sort_tbl
  1823. . PMA_Util::backquote(
  1824. $current_name
  1825. ) . ' ';
  1826. }
  1827. if ($is_in_sort) {
  1828. list($single_sort_order, $order_img)
  1829. = $this->_getSortingUrlParams(
  1830. $sort_direction, $single_sort_order,
  1831. $column_index, $index
  1832. );
  1833. } else {
  1834. $single_sort_order .= strtoupper($sort_direction[$index]);
  1835. }
  1836. }
  1837. if ($current_name == $name_to_use_in_sort && $is_in_sort) {
  1838. // We need to generate the arrow button and related html
  1839. list($sort_order, $order_img) = $this->_getSortingUrlParams(
  1840. $sort_direction, $sort_order, $column_index, $index
  1841. );
  1842. $order_img .= " <small>" . ($index + 1) . "</small>";
  1843. } else {
  1844. $sort_order .= strtoupper($sort_direction[$index]);
  1845. }
  1846. // Separate columns by a comma
  1847. $sort_order .= ", ";
  1848. unset($name_to_use_in_sort);
  1849. }
  1850. // remove the comma from the last column name in the newly
  1851. // constructed clause
  1852. $sort_order = /*overload*/mb_substr(
  1853. $sort_order,
  1854. 0,
  1855. /*overload*/mb_strlen($sort_order)-2
  1856. );
  1857. if (empty($order_img)) {
  1858. $order_img = '';
  1859. }
  1860. return array($single_sort_order, $sort_order, $order_img);
  1861. }
  1862. /**
  1863. * Check whether the column is sorted
  1864. *
  1865. * @param array $sort_expression sort expression
  1866. * @param array $sort_expression_nodirection sort expression without direction
  1867. * @param string $sort_tbl the table name
  1868. * @param string $name_to_use_in_sort the sorting column name
  1869. *
  1870. * @return boolean $is_in_sort the column sorted or not
  1871. *
  1872. * @access private
  1873. *
  1874. * @see _getTableHeaders()
  1875. */
  1876. private function _isInSorted(
  1877. $sort_expression, $sort_expression_nodirection, $sort_tbl,
  1878. $name_to_use_in_sort
  1879. ) {
  1880. $index_in_expression = 0;
  1881. foreach ($sort_expression_nodirection as $index => $clause) {
  1882. if (/*overload*/mb_strpos($clause, '.') !== false) {
  1883. $fragments = explode('.', $clause);
  1884. $clause2 = $fragments[0] . "." . str_replace('`', '', $fragments[1]);
  1885. } else {
  1886. $clause2 = $sort_tbl . str_replace('`', '', $clause);
  1887. }
  1888. if ($clause2 === $sort_tbl . $name_to_use_in_sort) {
  1889. $index_in_expression = $index;
  1890. break;
  1891. }
  1892. }
  1893. if (empty($sort_expression[$index_in_expression])) {
  1894. $is_in_sort = false;
  1895. } else {
  1896. // Field name may be preceded by a space, or any number
  1897. // of characters followed by a dot (tablename.fieldname)
  1898. // so do a direct comparison for the sort expression;
  1899. // this avoids problems with queries like
  1900. // "SELECT id, count(id)..." and clicking to sort
  1901. // on id or on count(id).
  1902. // Another query to test this:
  1903. // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p
  1904. // (and try clicking on each column's header twice)
  1905. $noSortTable = empty($sort_tbl) || /*overload*/mb_strpos(
  1906. $sort_expression_nodirection[$index_in_expression], $sort_tbl
  1907. ) === false;
  1908. $noOpenParenthesis = /*overload*/mb_strpos(
  1909. $sort_expression_nodirection[$index_in_expression], '('
  1910. ) === false;
  1911. if (! empty($sort_tbl) && $noSortTable && $noOpenParenthesis) {
  1912. $new_sort_expression_nodirection = $sort_tbl
  1913. . $sort_expression_nodirection[$index_in_expression];
  1914. } else {
  1915. $new_sort_expression_nodirection
  1916. = $sort_expression_nodirection[$index_in_expression];
  1917. }
  1918. //Back quotes are removed in next comparison, so remove them from value
  1919. //to compare.
  1920. $name_to_use_in_sort = str_replace('`', '', $name_to_use_in_sort);
  1921. $is_in_sort = false;
  1922. $sort_name = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort;
  1923. if ($sort_name == str_replace('`', '', $new_sort_expression_nodirection)
  1924. || $sort_name == str_replace('`', '', $sort_expression_nodirection[$index_in_expression])
  1925. ) {
  1926. $is_in_sort = true;
  1927. }
  1928. }
  1929. return $is_in_sort;
  1930. } // end of the '_isInSorted()' function
  1931. /**
  1932. * Get sort url parameters - sort order and order image
  1933. *
  1934. * @param array $sort_direction the sort direction
  1935. * @param string $sort_order the sorting order
  1936. * @param integer $column_index the index of the column
  1937. * @param integer $index the index of sort direction array.
  1938. *
  1939. * @return array 2 element array - $sort_order, $order_img
  1940. *
  1941. * @access private
  1942. *
  1943. * @see _getSingleAndMultiSortUrls()
  1944. */
  1945. private function _getSortingUrlParams(
  1946. $sort_direction, $sort_order, $column_index, $index
  1947. ) {
  1948. if (strtoupper(trim($sort_direction[$index])) == self::DESCENDING_SORT_DIR) {
  1949. $sort_order .= ' ASC';
  1950. $order_img = ' ' . PMA_Util::getImage(
  1951. 's_desc.png', __('Descending'),
  1952. array('class' => "soimg$column_index", 'title' => '')
  1953. );
  1954. $order_img .= ' ' . PMA_Util::getImage(
  1955. 's_asc.png', __('Ascending'),
  1956. array('class' => "soimg$column_index hide", 'title' => '')
  1957. );
  1958. } else {
  1959. $sort_order .= ' DESC';
  1960. $order_img = ' ' . PMA_Util::getImage(
  1961. 's_asc.png', __('Ascending'),
  1962. array('class' => "soimg$column_index", 'title' => '')
  1963. );
  1964. $order_img .= ' ' . PMA_Util::getImage(
  1965. 's_desc.png', __('Descending'),
  1966. array('class' => "soimg$column_index hide", 'title' => '')
  1967. );
  1968. }
  1969. return array($sort_order, $order_img);
  1970. } // end of the '_getSortingUrlParams()' function
  1971. /**
  1972. * Get sort order link
  1973. *
  1974. * @param string $order_img the sort order image
  1975. * @param integer $col_index the index of the column
  1976. * @param array $fields_meta set of field properties
  1977. * @param string $order_url the url for sort
  1978. * @param string $multi_order_url the url for sort
  1979. *
  1980. * @return string the sort order link
  1981. *
  1982. * @access private
  1983. *
  1984. * @see _getTableHeaders()
  1985. */
  1986. private function _getSortOrderLink(
  1987. $order_img, $col_index,
  1988. $fields_meta, $order_url, $multi_order_url
  1989. ) {
  1990. $order_link_params = array();
  1991. if (isset($order_img) && ($order_img != '')) {
  1992. if (/*overload*/mb_strstr($order_img, 'asc')) {
  1993. $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()";
  1994. $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()";
  1995. } elseif (/*overload*/mb_strstr($order_img, 'desc')) {
  1996. $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()";
  1997. $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()";
  1998. }
  1999. }
  2000. $order_link_content = htmlspecialchars($fields_meta->name);
  2001. $inner_link_content = $order_link_content . $order_img
  2002. . '<input type="hidden" value="' . $multi_order_url . '" />';
  2003. return PMA_Util::linkOrButton(
  2004. $order_url, $inner_link_content,
  2005. $order_link_params, false, true
  2006. );
  2007. } // end of the '_getSortOrderLink()' function
  2008. /**
  2009. * Check if the column contains numeric data. If yes, then set the
  2010. * column header's alignment right
  2011. *
  2012. * @param array $fields_meta set of field properties
  2013. * @param array &$th_class array containing classes
  2014. *
  2015. * @return void
  2016. *
  2017. * @see _getDraggableClassForSortableColumns()
  2018. */
  2019. private function _getClassForNumericColumnType($fields_meta,&$th_class)
  2020. {
  2021. if (preg_match(
  2022. '@int|decimal|float|double|real|bit|boolean|serial@i',
  2023. $fields_meta->type
  2024. )) {
  2025. $th_class[] = 'right';
  2026. }
  2027. }
  2028. /**
  2029. * Prepare columns to draggable effect for sortable columns
  2030. *
  2031. * @param boolean $col_visib the column is visible (false)
  2032. * array the column is not visible (string array)
  2033. * @param string $col_visib_j element of $col_visib array
  2034. * @param array $fields_meta set of field properties
  2035. * @param string $order_link the order link
  2036. * @param string $comments the comment for the column
  2037. *
  2038. * @return string $draggable_html html content
  2039. *
  2040. * @access private
  2041. *
  2042. * @see _getTableHeaders()
  2043. */
  2044. private function _getDraggableClassForSortableColumns(
  2045. $col_visib, $col_visib_j, $fields_meta,
  2046. $order_link, $comments
  2047. ) {
  2048. $draggable_html = '<th';
  2049. $th_class = array();
  2050. $th_class[] = 'draggable';
  2051. $this->_getClassForNumericColumnType($fields_meta, $th_class);
  2052. if ($col_visib && !$col_visib_j) {
  2053. $th_class[] = 'hide';
  2054. }
  2055. $th_class[] = 'column_heading';
  2056. if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) {
  2057. $th_class[] = 'pointer';
  2058. }
  2059. if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) {
  2060. $th_class[] = 'marker';
  2061. }
  2062. $draggable_html .= ' class="' . implode(' ', $th_class) . '"';
  2063. $draggable_html .= ' data-column="' . htmlspecialchars($fields_meta->name)
  2064. . '">' . $order_link . $comments . '</th>';
  2065. return $draggable_html;
  2066. } // end of the '_getDraggableClassForSortableColumns()' function
  2067. /**
  2068. * Prepare columns to draggable effect for non sortable columns
  2069. *
  2070. * @param boolean $col_visib the column is visible (false)
  2071. * array the column is not visible (string array)
  2072. * @param string $col_visib_j element of $col_visib array
  2073. * @param boolean $condition_field whether to add CSS class condition
  2074. * @param array $fields_meta set of field properties
  2075. * @param string $comments the comment for the column
  2076. *
  2077. * @return string $draggable_html html content
  2078. *
  2079. * @access private
  2080. *
  2081. * @see _getTableHeaders()
  2082. */
  2083. private function _getDraggableClassForNonSortableColumns(
  2084. $col_visib, $col_visib_j, $condition_field,
  2085. $fields_meta, $comments
  2086. ) {
  2087. $draggable_html = '<th';
  2088. $th_class = array();
  2089. $th_class[] = 'draggable';
  2090. $this->_getClassForNumericColumnType($fields_meta, $th_class);
  2091. if ($col_visib && !$col_visib_j) {
  2092. $th_class[] = 'hide';
  2093. }
  2094. if ($condition_field) {
  2095. $th_class[] = 'condition';
  2096. }
  2097. $draggable_html .= ' class="' . implode(' ', $th_class) . '"';
  2098. $draggable_html .= ' data-column="'
  2099. . htmlspecialchars($fields_meta->name) . '">';
  2100. $draggable_html .= htmlspecialchars($fields_meta->name);
  2101. $draggable_html .= "\n" . $comments . '</th>';
  2102. return $draggable_html;
  2103. } // end of the '_getDraggableClassForNonSortableColumns()' function
  2104. /**
  2105. * Prepare column to show at right side - check boxes or empty column
  2106. *
  2107. * @param array &$displayParts which elements to display
  2108. * @param string $full_or_partial_text_link full/partial link or text button
  2109. * @param string $colspan column span of table header
  2110. *
  2111. * @return string html content
  2112. *
  2113. * @access private
  2114. *
  2115. * @see _getTableHeaders()
  2116. */
  2117. private function _getColumnAtRightSide(
  2118. &$displayParts, $full_or_partial_text_link, $colspan
  2119. ) {
  2120. $right_column_html = '';
  2121. $display_params = $this->__get('display_params');
  2122. // Displays the needed checkboxes at the right
  2123. // column of the result table header if possible and required...
  2124. if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT)
  2125. || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
  2126. && (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  2127. || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE))
  2128. && ($displayParts['text_btn'] == '1')
  2129. ) {
  2130. $display_params['emptyafter']
  2131. = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  2132. && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 1;
  2133. $right_column_html .= "\n"
  2134. . '<th class="column_action print_ignore" ' . $colspan . '>'
  2135. . $full_or_partial_text_link
  2136. . '</th>';
  2137. } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
  2138. || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
  2139. && (($displayParts['edit_lnk'] == self::NO_EDIT_OR_DELETE)
  2140. && ($displayParts['del_lnk'] == self::NO_EDIT_OR_DELETE))
  2141. && (! isset($GLOBALS['is_header_sent']) || ! $GLOBALS['is_header_sent'])
  2142. ) {
  2143. // ... elseif no button, displays empty columns if required
  2144. // (unless coming from Browse mode print view)
  2145. $display_params['emptyafter']
  2146. = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  2147. && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 1;
  2148. $right_column_html .= "\n" . '<td class="print_ignore" ' . $colspan . '></td>';
  2149. }
  2150. $this->__set('display_params', $display_params);
  2151. return $right_column_html;
  2152. } // end of the '_getColumnAtRightSide()' function
  2153. /**
  2154. * Prepares the display for a value
  2155. *
  2156. * @param string $class class of table cell
  2157. * @param bool $condition_field whether to add CSS class condition
  2158. * @param string $value value to display
  2159. *
  2160. * @return string the td
  2161. *
  2162. * @access private
  2163. *
  2164. * @see _getDataCellForGeometryColumns(),
  2165. * _getDataCellForNonNumericColumns()
  2166. */
  2167. private function _buildValueDisplay($class, $condition_field, $value)
  2168. {
  2169. return '<td class="left ' . $class . ($condition_field ? ' condition' : '')
  2170. . '">' . $value . '</td>';
  2171. } // end of the '_buildValueDisplay()' function
  2172. /**
  2173. * Prepares the display for a null value
  2174. *
  2175. * @param string $class class of table cell
  2176. * @param bool $condition_field whether to add CSS class condition
  2177. * @param object $meta the meta-information about this field
  2178. * @param string $align cell alignment
  2179. *
  2180. * @return string the td
  2181. *
  2182. * @access private
  2183. *
  2184. * @see _getDataCellForNumericColumns(),
  2185. * _getDataCellForGeometryColumns(),
  2186. * _getDataCellForNonNumericColumns()
  2187. */
  2188. private function _buildNullDisplay($class, $condition_field, $meta, $align = '')
  2189. {
  2190. // the null class is needed for grid editing
  2191. $decimals = isset($meta->decimals) ? $meta->decimals : '-1';
  2192. return '<td ' . $align . ' data-decimals="' . $decimals
  2193. . '" data-type="' . $meta->type . '" class="'
  2194. . $this->_addClass(
  2195. $class, $condition_field, $meta, ''
  2196. )
  2197. . ' null"><i>NULL</i></td>';
  2198. } // end of the '_buildNullDisplay()' function
  2199. /**
  2200. * Prepares the display for an empty value
  2201. *
  2202. * @param string $class class of table cell
  2203. * @param bool $condition_field whether to add CSS class condition
  2204. * @param object $meta the meta-information about this field
  2205. * @param string $align cell alignment
  2206. *
  2207. * @return string the td
  2208. *
  2209. * @access private
  2210. *
  2211. * @see _getDataCellForNumericColumns(),
  2212. * _getDataCellForGeometryColumns(),
  2213. * _getDataCellForNonNumericColumns()
  2214. */
  2215. private function _buildEmptyDisplay($class, $condition_field, $meta, $align = '')
  2216. {
  2217. return '<td ' . $align . ' class="'
  2218. . $this->_addClass(
  2219. $class, $condition_field, $meta, 'nowrap'
  2220. )
  2221. . '"></td>';
  2222. } // end of the '_buildEmptyDisplay()' function
  2223. /**
  2224. * Adds the relevant classes.
  2225. *
  2226. * @param string $class class of table cell
  2227. * @param bool $condition_field whether to add CSS class
  2228. * condition
  2229. * @param object $meta the meta-information about the
  2230. * field
  2231. * @param string $nowrap avoid wrapping
  2232. * @param bool $is_field_truncated is field truncated (display ...)
  2233. * @param object|string $transformation_plugin transformation plugin.
  2234. * Can also be the default function:
  2235. * PMA_mimeDefaultFunction
  2236. * @param string $default_function default transformation function
  2237. *
  2238. * @return string the list of classes
  2239. *
  2240. * @access private
  2241. *
  2242. * @see _buildNullDisplay(), _getRowData()
  2243. */
  2244. private function _addClass(
  2245. $class, $condition_field, $meta, $nowrap, $is_field_truncated = false,
  2246. $transformation_plugin = '', $default_function = ''
  2247. ) {
  2248. $classes = array(
  2249. $class,
  2250. $nowrap,
  2251. );
  2252. if (isset($meta->mimetype)) {
  2253. $classes[] = preg_replace('/\//', '_', $meta->mimetype);
  2254. }
  2255. if ($condition_field) {
  2256. $classes[] = 'condition';
  2257. }
  2258. if ($is_field_truncated) {
  2259. $classes[] = 'truncated';
  2260. }
  2261. $mime_map = $this->__get('mime_map');
  2262. $orgFullColName = $this->__get('db') . '.' . $meta->orgtable
  2263. . '.' . $meta->orgname;
  2264. if ($transformation_plugin != $default_function
  2265. || !empty($mime_map[$orgFullColName]['input_transformation'])
  2266. ) {
  2267. $classes[] = 'transformed';
  2268. }
  2269. // Define classes to be added to this data field based on the type of data
  2270. $matches = array(
  2271. 'enum' => 'enum',
  2272. 'set' => 'set',
  2273. 'binary' => 'hex',
  2274. );
  2275. foreach ($matches as $key => $value) {
  2276. if (/*overload*/mb_strpos($meta->flags, $key) !== false) {
  2277. $classes[] = $value;
  2278. }
  2279. }
  2280. if (/*overload*/mb_strpos($meta->type, 'bit') !== false) {
  2281. $classes[] = 'bit';
  2282. }
  2283. return implode(' ', $classes);
  2284. } // end of the '_addClass()' function
  2285. /**
  2286. * Prepare the body of the results table
  2287. *
  2288. * @param integer &$dt_result the link id associated to the query which results have to be displayed
  2289. * which results have to be displayed
  2290. * @param array &$displayParts which elements to display
  2291. * @param array $map the list of relations
  2292. * @param array $analyzed_sql_results analyzed sql results
  2293. * @param boolean $is_limited_display with limited operations or not
  2294. *
  2295. * @return string $table_body_html html content
  2296. *
  2297. * @global array $row current row data
  2298. *
  2299. * @access private
  2300. *
  2301. * @see getTable()
  2302. */
  2303. private function _getTableBody(
  2304. &$dt_result, &$displayParts, $map, $analyzed_sql_results,
  2305. $is_limited_display = false
  2306. ) {
  2307. global $row; // mostly because of browser transformations,
  2308. // to make the row-data accessible in a plugin
  2309. $table_body_html = '';
  2310. // query without conditions to shorten URLs when needed, 200 is just
  2311. // guess, it should depend on remaining URL length
  2312. $url_sql_query = $this->_getUrlSqlQuery($analyzed_sql_results);
  2313. $display_params = $this->__get('display_params');
  2314. if (! is_array($map)) {
  2315. $map = array();
  2316. }
  2317. $row_no = 0;
  2318. $display_params['edit'] = array();
  2319. $display_params['copy'] = array();
  2320. $display_params['delete'] = array();
  2321. $display_params['data'] = array();
  2322. $display_params['row_delete'] = array();
  2323. $this->__set('display_params', $display_params);
  2324. // name of the class added to all grid editable elements;
  2325. // if we don't have all the columns of a unique key in the result set,
  2326. // do not permit grid editing
  2327. if ($is_limited_display || ! $this->__get('editable')) {
  2328. $grid_edit_class = '';
  2329. } else {
  2330. switch ($GLOBALS['cfg']['GridEditing']) {
  2331. case 'double-click':
  2332. // trying to reduce generated HTML by using shorter
  2333. // classes like click1 and click2
  2334. $grid_edit_class = 'grid_edit click2';
  2335. break;
  2336. case 'click':
  2337. $grid_edit_class = 'grid_edit click1';
  2338. break;
  2339. default: // 'disabled'
  2340. $grid_edit_class = '';
  2341. break;
  2342. }
  2343. }
  2344. // prepare to get the column order, if available
  2345. list($col_order, $col_visib) = $this->_getColumnParams($analyzed_sql_results);
  2346. // Correction University of Virginia 19991216 in the while below
  2347. // Previous code assumed that all tables have keys, specifically that
  2348. // the phpMyAdmin GUI should support row delete/edit only for such
  2349. // tables.
  2350. // Although always using keys is arguably the prescribed way of
  2351. // defining a relational table, it is not required. This will in
  2352. // particular be violated by the novice.
  2353. // We want to encourage phpMyAdmin usage by such novices. So the code
  2354. // below has been changed to conditionally work as before when the
  2355. // table being displayed has one or more keys; but to display
  2356. // delete/edit options correctly for tables without keys.
  2357. $odd_row = true;
  2358. $whereClauseMap = $this->__get('whereClauseMap');
  2359. while ($row = $GLOBALS['dbi']->fetchRow($dt_result)) {
  2360. // add repeating headers
  2361. if ((($row_no != 0) && ($_SESSION['tmpval']['repeat_cells'] != 0))
  2362. && !($row_no % $_SESSION['tmpval']['repeat_cells'])
  2363. ) {
  2364. $table_body_html .= $this->_getRepeatingHeaders(
  2365. $display_params
  2366. );
  2367. }
  2368. $tr_class = array();
  2369. if ($GLOBALS['cfg']['BrowsePointerEnable'] != true) {
  2370. $tr_class[] = 'nopointer';
  2371. }
  2372. if ($GLOBALS['cfg']['BrowseMarkerEnable'] != true) {
  2373. $tr_class[] = 'nomarker';
  2374. }
  2375. $tr_class[] = ($odd_row ? 'odd' : 'even');
  2376. $odd_row = ! $odd_row;
  2377. // pointer code part
  2378. $table_body_html .= '<tr class="' . implode(' ', $tr_class) . '">';
  2379. // 1. Prepares the row
  2380. // In print view these variable needs to be initialized
  2381. $del_url = $del_str = $edit_anchor_class
  2382. = $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null;
  2383. // 1.2 Defines the URLs for the modify/delete link(s)
  2384. if (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  2385. || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
  2386. ) {
  2387. // Results from a "SELECT" statement -> builds the
  2388. // WHERE clause to use in links (a unique key if possible)
  2389. /**
  2390. * @todo $where_clause could be empty, for example a table
  2391. * with only one field and it's a BLOB; in this case,
  2392. * avoid to display the delete and edit links
  2393. */
  2394. list($where_clause, $clause_is_unique, $condition_array)
  2395. = PMA_Util::getUniqueCondition(
  2396. $dt_result, // handle
  2397. $this->__get('fields_cnt'), // fields_cnt
  2398. $this->__get('fields_meta'), // fields_meta
  2399. $row, // row
  2400. false, // force_unique
  2401. $this->__get('table'), // restrict_to_table
  2402. $analyzed_sql_results // analyzed_sql_results
  2403. );
  2404. $whereClauseMap[$row_no][$this->__get('table')] = $where_clause;
  2405. $this->__set('whereClauseMap', $whereClauseMap);
  2406. $where_clause_html = urlencode($where_clause);
  2407. // 1.2.1 Modify link(s) - update row case
  2408. if ($displayParts['edit_lnk'] == self::UPDATE_ROW) {
  2409. list($edit_url, $copy_url, $edit_str, $copy_str,
  2410. $edit_anchor_class)
  2411. = $this->_getModifiedLinks(
  2412. $where_clause,
  2413. $clause_is_unique, $url_sql_query
  2414. );
  2415. } // end if (1.2.1)
  2416. // 1.2.2 Delete/Kill link(s)
  2417. list($del_url, $del_str, $js_conf)
  2418. = $this->_getDeleteAndKillLinks(
  2419. $where_clause, $clause_is_unique,
  2420. $url_sql_query, $displayParts['del_lnk'],
  2421. $row
  2422. );
  2423. // 1.3 Displays the links at left if required
  2424. if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
  2425. || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)
  2426. ) {
  2427. $table_body_html .= $this->_getPlacedLinks(
  2428. self::POSITION_LEFT, $del_url, $displayParts, $row_no,
  2429. $where_clause, $where_clause_html, $condition_array,
  2430. $edit_url, $copy_url, $edit_anchor_class,
  2431. $edit_str, $copy_str, $del_str, $js_conf
  2432. );
  2433. } elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) {
  2434. $table_body_html .= $this->_getPlacedLinks(
  2435. self::POSITION_NONE, $del_url, $displayParts, $row_no,
  2436. $where_clause, $where_clause_html, $condition_array,
  2437. $edit_url, $copy_url, $edit_anchor_class,
  2438. $edit_str, $copy_str, $del_str, $js_conf
  2439. );
  2440. } // end if (1.3)
  2441. } // end if (1)
  2442. // 2. Displays the rows' values
  2443. if (is_null($this->__get('mime_map'))) {
  2444. $this->_setMimeMap();
  2445. }
  2446. $table_body_html .= $this->_getRowValues(
  2447. $dt_result,
  2448. $row,
  2449. $row_no,
  2450. $col_order,
  2451. $map,
  2452. $grid_edit_class,
  2453. $col_visib,
  2454. $url_sql_query,
  2455. $analyzed_sql_results
  2456. );
  2457. // 3. Displays the modify/delete links on the right if required
  2458. if (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
  2459. || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
  2460. ) {
  2461. if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT)
  2462. || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)
  2463. ) {
  2464. $table_body_html .= $this->_getPlacedLinks(
  2465. self::POSITION_RIGHT, $del_url, $displayParts, $row_no,
  2466. $where_clause, $where_clause_html, $condition_array,
  2467. $edit_url, $copy_url, $edit_anchor_class,
  2468. $edit_str, $copy_str, $del_str, $js_conf
  2469. );
  2470. }
  2471. } // end if (3)
  2472. $table_body_html .= '</tr>';
  2473. $table_body_html .= "\n";
  2474. $row_no++;
  2475. } // end while
  2476. return $table_body_html;
  2477. } // end of the '_getTableBody()' function
  2478. /**
  2479. * Sets the MIME details of the columns in the results set
  2480. *
  2481. * @return void
  2482. */
  2483. private function _setMimeMap()
  2484. {
  2485. $fields_meta = $this->__get('fields_meta');
  2486. $mimeMap = array();
  2487. $added = array();
  2488. for ($currentColumn = 0;
  2489. $currentColumn < $this->__get('fields_cnt');
  2490. ++$currentColumn) {
  2491. $meta = $fields_meta[$currentColumn];
  2492. $orgFullTableName = $this->__get('db') . '.' . $meta->orgtable;
  2493. if ($GLOBALS['cfgRelation']['commwork']
  2494. && $GLOBALS['cfgRelation']['mimework']
  2495. && $GLOBALS['cfg']['BrowseMIME']
  2496. && ! $_SESSION['tmpval']['hide_transformation']
  2497. && empty($added[$orgFullTableName])
  2498. ) {
  2499. $mimeMap = array_merge(
  2500. $mimeMap,
  2501. PMA_getMIME($this->__get('db'), $meta->orgtable, false, true)
  2502. );
  2503. $added[$orgFullTableName] = true;
  2504. }
  2505. }
  2506. // special browser transformation for some SHOW statements
  2507. if ($this->__get('is_show')
  2508. && ! $_SESSION['tmpval']['hide_transformation']
  2509. ) {
  2510. preg_match(
  2511. '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
  2512. . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
  2513. . ')@i',
  2514. $this->__get('sql_query'), $which
  2515. );
  2516. if (isset($which[1])) {
  2517. $str = ' ' . strtoupper($which[1]);
  2518. $isShowProcessList = strpos($str, 'PROCESSLIST') > 0;
  2519. if ($isShowProcessList) {
  2520. $mimeMap['..Info'] = array(
  2521. 'mimetype' => 'Text_Plain',
  2522. 'transformation' => 'output/Text_Plain_Sql.class.php',
  2523. );
  2524. }
  2525. $isShowCreateTable = preg_match(
  2526. '@CREATE[[:space:]]+TABLE@i', $this->__get('sql_query')
  2527. );
  2528. if ($isShowCreateTable) {
  2529. $mimeMap['..Create Table'] = array(
  2530. 'mimetype' => 'Text_Plain',
  2531. 'transformation' => 'output/Text_Plain_Sql.class.php',
  2532. );
  2533. }
  2534. }
  2535. }
  2536. $this->__set('mime_map', $mimeMap);
  2537. }
  2538. /**
  2539. * Get the values for one data row
  2540. *
  2541. * @param integer &$dt_result the link id associated to the query which results have to be displayed
  2542. * which results have to be displayed
  2543. * @param array $row current row data
  2544. * @param integer $row_no the index of current row
  2545. * @param array $col_order the column order false when a property not found
  2546. * false when a property not found
  2547. * @param array $map the list of relations
  2548. * @param string $grid_edit_class the class for all editable columns
  2549. * @param boolean $col_visib column is visible(false) array column isn't visible(string array)
  2550. * array column isn't visible(string array)
  2551. * @param string $url_sql_query the analyzed sql query
  2552. * @param array $analyzed_sql_results analyzed sql results
  2553. *
  2554. * @return string $row_values_html html content
  2555. *
  2556. * @access private
  2557. *
  2558. * @see _getTableBody()
  2559. */
  2560. private function _getRowValues(
  2561. &$dt_result, $row, $row_no, $col_order, $map,
  2562. $grid_edit_class, $col_visib,
  2563. $url_sql_query, $analyzed_sql_results
  2564. ) {
  2565. $row_values_html = '';
  2566. // Following variable are needed for use in isset/empty or
  2567. // use with array indexes/safe use in foreach
  2568. $sql_query = $this->__get('sql_query');
  2569. $fields_meta = $this->__get('fields_meta');
  2570. $highlight_columns = $this->__get('highlight_columns');
  2571. $mime_map = $this->__get('mime_map');
  2572. $row_info = $this->_getRowInfoForSpecialLinks($row, $col_order);
  2573. $whereClauseMap = $this->__get('whereClauseMap');
  2574. $columnCount = $this->__get('fields_cnt');
  2575. for ($currentColumn = 0;
  2576. $currentColumn < $columnCount;
  2577. ++$currentColumn) {
  2578. // assign $i with appropriate column order
  2579. $i = $col_order ? $col_order[$currentColumn] : $currentColumn;
  2580. $meta = $fields_meta[$i];
  2581. $orgFullColName
  2582. = $this->__get('db') . '.' . $meta->orgtable . '.' . $meta->orgname;
  2583. $not_null_class = $meta->not_null ? 'not_null' : '';
  2584. $relation_class = isset($map[$meta->name]) ? 'relation' : '';
  2585. $hide_class = ($col_visib && ! $col_visib[$currentColumn])
  2586. ? 'hide'
  2587. : '';
  2588. $grid_edit = $meta->orgtable != '' ? $grid_edit_class : '';
  2589. // handle datetime-related class, for grid editing
  2590. $field_type_class
  2591. = $this->_getClassForDateTimeRelatedFields($meta->type);
  2592. $is_field_truncated = false;
  2593. // combine all the classes applicable to this column's value
  2594. $class = $this->_getClassesForColumn(
  2595. $grid_edit, $not_null_class, $relation_class,
  2596. $hide_class, $field_type_class
  2597. );
  2598. // See if this column should get highlight because it's used in the
  2599. // where-query.
  2600. $condition_field = (isset($highlight_columns)
  2601. && (isset($highlight_columns[$meta->name])
  2602. || isset($highlight_columns[PMA_Util::backquote($meta->name)])))
  2603. ? true
  2604. : false;
  2605. // Wrap MIME-transformations. [MIME]
  2606. $default_function = 'PMA_mimeDefaultFunction'; // default_function
  2607. $transformation_plugin = $default_function;
  2608. $transform_options = array();
  2609. if ($GLOBALS['cfgRelation']['mimework']
  2610. && $GLOBALS['cfg']['BrowseMIME']
  2611. ) {
  2612. if (isset($mime_map[$orgFullColName]['mimetype'])
  2613. && !empty($mime_map[$orgFullColName]['transformation'])
  2614. ) {
  2615. $file = $mime_map[$orgFullColName]['transformation'];
  2616. $include_file = 'libraries/plugins/transformations/' . $file;
  2617. if (file_exists($include_file)) {
  2618. include_once $include_file;
  2619. $class_name = PMA_getTransformationClassName($file);
  2620. // todo add $plugin_manager
  2621. $plugin_manager = null;
  2622. $transformation_plugin = new $class_name(
  2623. $plugin_manager
  2624. );
  2625. $transform_options = PMA_Transformation_getOptions(
  2626. isset($mime_map[$orgFullColName]
  2627. ['transformation_options']
  2628. )
  2629. ? $mime_map[$orgFullColName]
  2630. ['transformation_options']
  2631. : ''
  2632. );
  2633. $meta->mimetype = str_replace(
  2634. '_', '/',
  2635. $mime_map[$orgFullColName]['mimetype']
  2636. );
  2637. } // end if file_exists
  2638. } // end if transformation is set
  2639. } // end if mime/transformation works.
  2640. // Check whether the field needs to display with syntax highlighting
  2641. $dbLower = /*overload*/mb_strtolower($this->__get('db'));
  2642. $tblLower = /*overload*/mb_strtolower($meta->orgtable);
  2643. $nameLower = /*overload*/mb_strtolower($meta->orgname);
  2644. if (! empty($this->transformation_info[$dbLower][$tblLower][$nameLower])
  2645. && (trim($row[$i]) != '')
  2646. && ! $_SESSION['tmpval']['hide_transformation']
  2647. ) {
  2648. include_once $this->transformation_info
  2649. [$dbLower][$tblLower][$nameLower][0];
  2650. $transformation_plugin = new $this->transformation_info
  2651. [$dbLower][$tblLower][$nameLower][1](null);
  2652. $transform_options = PMA_Transformation_getOptions(
  2653. isset($mime_map[$orgFullColName]['transformation_options'])
  2654. ? $mime_map[$orgFullColName]['transformation_options']
  2655. : ''
  2656. );
  2657. $meta->mimetype = str_replace(
  2658. '_', '/',
  2659. $this->transformation_info[$dbLower]
  2660. [/*overload*/mb_strtolower($meta->orgtable)]
  2661. [/*overload*/mb_strtolower($meta->orgname)][2]
  2662. );
  2663. }
  2664. // Check for the predefined fields need to show as link in schemas
  2665. include_once 'libraries/special_schema_links.lib.php';
  2666. if (isset($GLOBALS['special_schema_links'])
  2667. && (! empty($GLOBALS['special_schema_links'][$dbLower][$tblLower][$nameLower]))
  2668. ) {
  2669. $linking_url = $this->_getSpecialLinkUrl(
  2670. $row[$i], $row_info, /*overload*/mb_strtolower($meta->orgname)
  2671. );
  2672. include_once
  2673. "libraries/plugins/transformations/Text_Plain_Link.class.php";
  2674. $transformation_plugin = new Text_Plain_Link();
  2675. $transform_options = array(
  2676. 0 => $linking_url,
  2677. 2 => true
  2678. );
  2679. $meta->mimetype = str_replace(
  2680. '_', '/',
  2681. 'Text/Plain'
  2682. );
  2683. }
  2684. /*
  2685. * The result set can have columns from more than one table,
  2686. * this is why we have to check for the unique conditions
  2687. * related to this table; however getUniqueCondition() is
  2688. * costly and does not need to be called if we already know
  2689. * the conditions for the current table.
  2690. */
  2691. if (! isset($whereClauseMap[$row_no][$meta->orgtable])) {
  2692. $unique_conditions = PMA_Util::getUniqueCondition(
  2693. $dt_result, // handle
  2694. $this->__get('fields_cnt'), // fields_cnt
  2695. $this->__get('fields_meta'), // fields_meta
  2696. $row, // row
  2697. false, // force_unique
  2698. $meta->orgtable, // restrict_to_table
  2699. $analyzed_sql_results // analyzed_sql_results
  2700. );
  2701. $whereClauseMap[$row_no][$meta->orgtable] = $unique_conditions[0];
  2702. }
  2703. $_url_params = array(
  2704. 'db' => $this->__get('db'),
  2705. 'table' => $meta->orgtable,
  2706. 'where_clause' => $whereClauseMap[$row_no][$meta->orgtable],
  2707. 'transform_key' => $meta->orgname
  2708. );
  2709. if (! empty($sql_query)) {
  2710. $_url_params['sql_query'] = $url_sql_query;
  2711. }
  2712. $transform_options['wrapper_link']
  2713. = PMA_URL_getCommon($_url_params);
  2714. $display_params = $this->__get('display_params');
  2715. // in some situations (issue 11406), numeric returns 1
  2716. // even for a string type
  2717. if ($meta->numeric == 1 && $meta->type != 'string') {
  2718. // n u m e r i c
  2719. $display_params['data'][$row_no][$i]
  2720. = $this->_getDataCellForNumericColumns(
  2721. $row[$i],
  2722. $class,
  2723. $condition_field,
  2724. $meta,
  2725. $map,
  2726. $is_field_truncated,
  2727. $analyzed_sql_results,
  2728. $transformation_plugin,
  2729. $default_function,
  2730. $transform_options
  2731. );
  2732. } elseif ($meta->type == self::GEOMETRY_FIELD) {
  2733. // g e o m e t r y
  2734. // Remove 'grid_edit' from $class as we do not allow to
  2735. // inline-edit geometry data.
  2736. $class = str_replace('grid_edit', '', $class);
  2737. $display_params['data'][$row_no][$i]
  2738. = $this->_getDataCellForGeometryColumns(
  2739. $row[$i],
  2740. $class,
  2741. $meta,
  2742. $map,
  2743. $_url_params,
  2744. $condition_field,
  2745. $transformation_plugin,
  2746. $default_function,
  2747. $transform_options,
  2748. $analyzed_sql_results
  2749. );
  2750. } else {
  2751. // n o t n u m e r i c
  2752. $display_params['data'][$row_no][$i]
  2753. = $this->_getDataCellForNonNumericColumns(
  2754. $row[$i],
  2755. $class,
  2756. $meta,
  2757. $map,
  2758. $_url_params,
  2759. $condition_field,
  2760. $transformation_plugin,
  2761. $default_function,
  2762. $transform_options,
  2763. $is_field_truncated,
  2764. $analyzed_sql_results,
  2765. $dt_result,
  2766. $i
  2767. );
  2768. }
  2769. // output stored cell
  2770. $row_values_html .= $display_params['data'][$row_no][$i];
  2771. if (isset($display_params['rowdata'][$i][$row_no])) {
  2772. $display_params['rowdata'][$i][$row_no]
  2773. .= $display_params['data'][$row_no][$i];
  2774. } else {
  2775. $display_params['rowdata'][$i][$row_no]
  2776. = $display_params['data'][$row_no][$i];
  2777. }
  2778. $this->__set('display_params', $display_params);
  2779. } // end for
  2780. return $row_values_html;
  2781. } // end of the '_getRowValues()' function
  2782. /**
  2783. * Get link for display special schema links
  2784. *
  2785. * @param string $column_value column value
  2786. * @param array $row_info information about row
  2787. * @param string $field_name column name
  2788. *
  2789. * @return string generated link
  2790. */
  2791. private function _getSpecialLinkUrl($column_value, $row_info, $field_name)
  2792. {
  2793. $linking_url_params = array();
  2794. $link_relations = $GLOBALS['special_schema_links']
  2795. [/*overload*/mb_strtolower($this->__get('db'))]
  2796. [/*overload*/mb_strtolower($this->__get('table'))]
  2797. [$field_name];
  2798. if (! is_array($link_relations['link_param'])) {
  2799. $linking_url_params[$link_relations['link_param']] = $column_value;
  2800. } else {
  2801. // Consider only the case of creating link for column field
  2802. // sql query that needs to be passed as url param
  2803. $sql = 'SELECT `' . $column_value . '` FROM `'
  2804. . $row_info[$link_relations['link_param'][1]] . '`.`'
  2805. . $row_info[$link_relations['link_param'][2]] . '`';
  2806. $linking_url_params[$link_relations['link_param'][0]] = $sql;
  2807. }
  2808. $divider = strpos($link_relations['default_page'], '?') ? '&' : '?';
  2809. if (empty($link_relations['link_dependancy_params'])) {
  2810. return $link_relations['default_page']
  2811. . PMA_URL_getCommon($linking_url_params, 'html', $divider);
  2812. }
  2813. foreach ($link_relations['link_dependancy_params'] as $new_param) {
  2814. // If param_info is an array, set the key and value
  2815. // from that array
  2816. if (is_array($new_param['param_info'])) {
  2817. $linking_url_params[$new_param['param_info'][0]]
  2818. = $new_param['param_info'][1];
  2819. continue;
  2820. }
  2821. $linking_url_params[$new_param['param_info']]
  2822. = $row_info[/*overload*/mb_strtolower($new_param['column_name'])];
  2823. // Special case 1 - when executing routines, according
  2824. // to the type of the routine, url param changes
  2825. if (empty($row_info['routine_type'])) {
  2826. continue;
  2827. }
  2828. }
  2829. return $link_relations['default_page']
  2830. . PMA_URL_getCommon($linking_url_params, 'html', $divider);
  2831. }
  2832. /**
  2833. * Prepare row information for display special links
  2834. *
  2835. * @param array $row current row data
  2836. * @param array $col_order the column order
  2837. *
  2838. * @return array $row_info associative array with column nama -> value
  2839. */
  2840. private function _getRowInfoForSpecialLinks($row, $col_order)
  2841. {
  2842. $row_info = array();
  2843. $fields_meta = $this->__get('fields_meta');
  2844. for ($n = 0; $n < $this->__get('fields_cnt'); ++$n) {
  2845. $m = $col_order ? $col_order[$n] : $n;
  2846. $row_info[/*overload*/mb_strtolower($fields_meta[$m]->name)]
  2847. = $row[$m];
  2848. }
  2849. return $row_info;
  2850. }
  2851. /**
  2852. * Get url sql query without conditions to shorten URLs
  2853. *
  2854. * @param array $analyzed_sql_results analyzed sql results
  2855. *
  2856. * @return string $url_sql analyzed sql query
  2857. *
  2858. * @access private
  2859. *
  2860. * @see _getTableBody()
  2861. */
  2862. private function _getUrlSqlQuery($analyzed_sql_results)
  2863. {
  2864. if (($analyzed_sql_results['querytype'] != 'SELECT')
  2865. || (/*overload*/mb_strlen($this->__get('sql_query')) < 200)
  2866. ) {
  2867. return $this->__get('sql_query');
  2868. }
  2869. $query = 'SELECT ' . SqlParser\Utils\Query::getClause(
  2870. $analyzed_sql_results['statement'],
  2871. $analyzed_sql_results['parser']->list,
  2872. 'SELECT'
  2873. );
  2874. $from_clause = SqlParser\Utils\Query::getClause(
  2875. $analyzed_sql_results['statement'],
  2876. $analyzed_sql_results['parser']->list,
  2877. 'FROM'
  2878. );
  2879. if (!empty($from_clause)) {
  2880. $query .= ' FROM ' . $from_clause;
  2881. }
  2882. return $query;
  2883. } // end of the '_getUrlSqlQuery()' function
  2884. /**
  2885. * Get column order and column visibility
  2886. *
  2887. * @param array $analyzed_sql_results analyzed sql results
  2888. *
  2889. * @return array 2 element array - $col_order, $col_visib
  2890. *
  2891. * @access private
  2892. *
  2893. * @see _getTableBody()
  2894. */
  2895. private function _getColumnParams($analyzed_sql_results)
  2896. {
  2897. if ($this->_isSelect($analyzed_sql_results)) {
  2898. $pmatable = new PMA_Table($this->__get('table'), $this->__get('db'));
  2899. $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER);
  2900. $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB);
  2901. } else {
  2902. $col_order = false;
  2903. $col_visib = false;
  2904. }
  2905. return array($col_order, $col_visib);
  2906. } // end of the '_getColumnParams()' function
  2907. /**
  2908. * Get HTML for repeating headers
  2909. *
  2910. * @param array $display_params holds various display info
  2911. *
  2912. * @return string $header_html html content
  2913. *
  2914. * @access private
  2915. *
  2916. * @see _getTableBody()
  2917. */
  2918. private function _getRepeatingHeaders(
  2919. $display_params
  2920. ) {
  2921. $header_html = '<tr>' . "\n";
  2922. if ($display_params['emptypre'] > 0) {
  2923. $header_html .= ' <th colspan="'
  2924. . $display_params['emptypre'] . '">'
  2925. . "\n" . ' &nbsp;</th>' . "\n";
  2926. } else if ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) {
  2927. $header_html .= ' <th></th>' . "\n";
  2928. }
  2929. foreach ($display_params['desc'] as $val) {
  2930. $header_html .= $val;
  2931. }
  2932. if ($display_params['emptyafter'] > 0) {
  2933. $header_html
  2934. .= ' <th colspan="' . $display_params['emptyafter']
  2935. . '">'
  2936. . "\n" . ' &nbsp;</th>' . "\n";
  2937. }
  2938. $header_html .= '</tr>' . "\n";
  2939. return $header_html;
  2940. } // end of the '_getRepeatingHeaders()' function
  2941. /**
  2942. * Get modified links
  2943. *
  2944. * @param string $where_clause the where clause of the sql
  2945. * @param boolean $clause_is_unique the unique condition of clause
  2946. * @param string $url_sql_query the analyzed sql query
  2947. *
  2948. * @return array 5 element array - $edit_url, $copy_url,
  2949. * $edit_str, $copy_str, $edit_anchor_class
  2950. *
  2951. * @access private
  2952. *
  2953. * @see _getTableBody()
  2954. */
  2955. private function _getModifiedLinks(
  2956. $where_clause, $clause_is_unique, $url_sql_query
  2957. ) {
  2958. $_url_params = array(
  2959. 'db' => $this->__get('db'),
  2960. 'table' => $this->__get('table'),
  2961. 'where_clause' => $where_clause,
  2962. 'clause_is_unique' => $clause_is_unique,
  2963. 'sql_query' => $url_sql_query,
  2964. 'goto' => 'sql.php',
  2965. );
  2966. $edit_url = 'tbl_change.php'
  2967. . PMA_URL_getCommon(
  2968. $_url_params + array('default_action' => 'update')
  2969. );
  2970. $copy_url = 'tbl_change.php'
  2971. . PMA_URL_getCommon(
  2972. $_url_params + array('default_action' => 'insert')
  2973. );
  2974. $edit_str = $this->_getActionLinkContent(
  2975. 'b_edit.png', __('Edit')
  2976. );
  2977. $copy_str = $this->_getActionLinkContent(
  2978. 'b_insrow.png', __('Copy')
  2979. );
  2980. // Class definitions required for grid editing jQuery scripts
  2981. $edit_anchor_class = "edit_row_anchor";
  2982. if ($clause_is_unique == 0) {
  2983. $edit_anchor_class .= ' nonunique';
  2984. }
  2985. return array($edit_url, $copy_url, $edit_str, $copy_str, $edit_anchor_class);
  2986. } // end of the '_getModifiedLinks()' function
  2987. /**
  2988. * Get delete and kill links
  2989. *
  2990. * @param string $where_clause the where clause of the sql
  2991. * @param boolean $clause_is_unique the unique condition of clause
  2992. * @param string $url_sql_query the analyzed sql query
  2993. * @param string $del_lnk the delete link of current row
  2994. * @param array $row the current row
  2995. *
  2996. * @return array 3 element array
  2997. * $del_url, $del_str, $js_conf
  2998. *
  2999. * @access private
  3000. *
  3001. * @see _getTableBody()
  3002. */
  3003. private function _getDeleteAndKillLinks(
  3004. $where_clause, $clause_is_unique, $url_sql_query, $del_lnk, $row
  3005. ) {
  3006. $goto = $this->__get('goto');
  3007. if ($del_lnk == self::DELETE_ROW) { // delete row case
  3008. $_url_params = array(
  3009. 'db' => $this->__get('db'),
  3010. 'table' => $this->__get('table'),
  3011. 'sql_query' => $url_sql_query,
  3012. 'message_to_show' => __('The row has been deleted.'),
  3013. 'goto' => (empty($goto) ? 'tbl_sql.php' : $goto),
  3014. );
  3015. $lnk_goto = 'sql.php' . PMA_URL_getCommon($_url_params, 'text');
  3016. $del_query = 'DELETE FROM '
  3017. . PMA_Util::backquote($this->__get('table'))
  3018. . ' WHERE ' . $where_clause .
  3019. ($clause_is_unique ? '' : ' LIMIT 1');
  3020. $_url_params = array(
  3021. 'db' => $this->__get('db'),
  3022. 'table' => $this->__get('table'),
  3023. 'sql_query' => $del_query,
  3024. 'message_to_show' => __('The row has been deleted.'),
  3025. 'goto' => $lnk_goto,
  3026. );
  3027. $del_url = 'sql.php' . PMA_URL_getCommon($_url_params);
  3028. $js_conf = 'DELETE FROM ' . PMA_jsFormat($this->__get('table'))
  3029. . ' WHERE ' . PMA_jsFormat($where_clause, false)
  3030. . ($clause_is_unique ? '' : ' LIMIT 1');
  3031. $del_str = $this->_getActionLinkContent('b_drop.png', __('Delete'));
  3032. } elseif ($del_lnk == self::KILL_PROCESS) { // kill process case
  3033. $_url_params = array(
  3034. 'db' => $this->__get('db'),
  3035. 'table' => $this->__get('table'),
  3036. 'sql_query' => $url_sql_query,
  3037. 'goto' => 'index.php',
  3038. );
  3039. $lnk_goto = 'sql.php'
  3040. . PMA_URL_getCommon(
  3041. $_url_params, 'text'
  3042. );
  3043. $kill = $GLOBALS['dbi']->getKillQuery($row[0]);
  3044. $_url_params = array(
  3045. 'db' => 'mysql',
  3046. 'sql_query' => $kill,
  3047. 'goto' => $lnk_goto,
  3048. );
  3049. $del_url = 'sql.php' . PMA_URL_getCommon($_url_params);
  3050. $js_conf = $kill;
  3051. $del_str = PMA_Util::getIcon(
  3052. 'b_drop.png', __('Kill')
  3053. );
  3054. } else {
  3055. $del_url = $del_str = $js_conf = null;
  3056. }
  3057. return array($del_url, $del_str, $js_conf);
  3058. } // end of the '_getDeleteAndKillLinks()' function
  3059. /**
  3060. * Get content inside the table row action links (Edit/Copy/Delete)
  3061. *
  3062. * @param string $icon The name of the file to get
  3063. * @param string $display_text The text displaying after the image icon
  3064. *
  3065. * @return string
  3066. *
  3067. * @access private
  3068. *
  3069. * @see _getModifiedLinks(), _getDeleteAndKillLinks()
  3070. */
  3071. private function _getActionLinkContent($icon, $display_text)
  3072. {
  3073. $linkContent = '';
  3074. if (isset($GLOBALS['cfg']['RowActionType'])
  3075. && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_ICONS
  3076. ) {
  3077. $linkContent .= '<span class="nowrap">'
  3078. . PMA_Util::getImage(
  3079. $icon, $display_text
  3080. )
  3081. . '</span>';
  3082. } else if (isset($GLOBALS['cfg']['RowActionType'])
  3083. && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_TEXT
  3084. ) {
  3085. $linkContent .= '<span class="nowrap">' . $display_text . '</span>';
  3086. } else {
  3087. $linkContent .= PMA_Util::getIcon(
  3088. $icon, $display_text
  3089. );
  3090. }
  3091. return $linkContent;
  3092. }
  3093. /**
  3094. * Prepare placed links
  3095. *
  3096. * @param string $dir the direction of links should place
  3097. * @param string $del_url the url for delete row
  3098. * @param array $displayParts which elements to display
  3099. * @param integer $row_no the index of current row
  3100. * @param string $where_clause the where clause of the sql
  3101. * @param string $where_clause_html the html encoded where clause
  3102. * @param array $condition_array array of keys (primary, unique, condition)
  3103. * @param string $edit_url the url for edit row
  3104. * @param string $copy_url the url for copy row
  3105. * @param string $edit_anchor_class the class for html element for edit
  3106. * @param string $edit_str the label for edit row
  3107. * @param string $copy_str the label for copy row
  3108. * @param string $del_str the label for delete row
  3109. * @param string $js_conf text for the JS confirmation
  3110. *
  3111. * @return string html content
  3112. *
  3113. * @access private
  3114. *
  3115. * @see _getTableBody()
  3116. */
  3117. private function _getPlacedLinks(
  3118. $dir, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html,
  3119. $condition_array, $edit_url, $copy_url,
  3120. $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf
  3121. ) {
  3122. if (! isset($js_conf)) {
  3123. $js_conf = '';
  3124. }
  3125. return $this->_getCheckboxAndLinks(
  3126. $dir, $del_url, $displayParts,
  3127. $row_no, $where_clause, $where_clause_html, $condition_array,
  3128. $edit_url, $copy_url, $edit_anchor_class,
  3129. $edit_str, $copy_str, $del_str, $js_conf
  3130. );
  3131. } // end of the '_getPlacedLinks()' function
  3132. /**
  3133. * Get the combined classes for a column
  3134. *
  3135. * @param string $grid_edit_class the class for all editable columns
  3136. * @param string $not_null_class the class for not null columns
  3137. * @param string $relation_class the class for relations in a column
  3138. * @param string $hide_class the class for visibility of a column
  3139. * @param string $field_type_class the class related to type of the field
  3140. *
  3141. * @return string $class the combined classes
  3142. *
  3143. * @access private
  3144. *
  3145. * @see _getTableBody()
  3146. */
  3147. private function _getClassesForColumn(
  3148. $grid_edit_class, $not_null_class, $relation_class,
  3149. $hide_class, $field_type_class
  3150. ) {
  3151. $class = 'data ' . $grid_edit_class . ' ' . $not_null_class . ' '
  3152. . $relation_class . ' ' . $hide_class . ' ' . $field_type_class;
  3153. return $class;
  3154. } // end of the '_getClassesForColumn()' function
  3155. /**
  3156. * Get class for datetime related fields
  3157. *
  3158. * @param string $type the type of the column field
  3159. *
  3160. * @return string $field_type_class the class for the column
  3161. *
  3162. * @access private
  3163. *
  3164. * @see _getTableBody()
  3165. */
  3166. private function _getClassForDateTimeRelatedFields($type)
  3167. {
  3168. if ((substr($type, 0, 9) == self::TIMESTAMP_FIELD)
  3169. || ($type == self::DATETIME_FIELD)
  3170. ) {
  3171. $field_type_class = 'datetimefield';
  3172. } elseif ($type == self::DATE_FIELD) {
  3173. $field_type_class = 'datefield';
  3174. } elseif ($type == self::TIME_FIELD) {
  3175. $field_type_class = 'timefield';
  3176. } elseif ($type == self::STRING_FIELD) {
  3177. $field_type_class = 'text';
  3178. } else {
  3179. $field_type_class = '';
  3180. }
  3181. return $field_type_class;
  3182. } // end of the '_getClassForDateTimeRelatedFields()' function
  3183. /**
  3184. * Prepare data cell for numeric type fields
  3185. *
  3186. * @param string $column the column's value
  3187. * @param string $class the html class for column
  3188. * @param boolean $condition_field the column should highlighted
  3189. * or not
  3190. * @param object $meta the meta-information about this
  3191. * field
  3192. * @param array $map the list of relations
  3193. * @param boolean $is_field_truncated the condition for blob data
  3194. * replacements
  3195. * @param array $analyzed_sql_results the analyzed query
  3196. * @param object|string $transformation_plugin the name of transformation plugin
  3197. * @param string $default_function the default transformation
  3198. * function
  3199. * @param string $transform_options the transformation parameters
  3200. *
  3201. * @return string $cell the prepared cell, html content
  3202. *
  3203. * @access private
  3204. *
  3205. * @see _getTableBody()
  3206. */
  3207. private function _getDataCellForNumericColumns(
  3208. $column, $class, $condition_field, $meta, $map, $is_field_truncated,
  3209. $analyzed_sql_results, $transformation_plugin, $default_function,
  3210. $transform_options
  3211. ) {
  3212. if (! isset($column) || is_null($column)) {
  3213. $cell = $this->_buildNullDisplay(
  3214. 'right ' . $class, $condition_field, $meta, ''
  3215. );
  3216. } elseif ($column != '') {
  3217. $nowrap = ' nowrap';
  3218. $where_comparison = ' = ' . $column;
  3219. $cell = $this->_getRowData(
  3220. 'right ' . $class, $condition_field,
  3221. $analyzed_sql_results, $meta, $map, $column,
  3222. $transformation_plugin, $default_function, $nowrap,
  3223. $where_comparison, $transform_options,
  3224. $is_field_truncated, ''
  3225. );
  3226. } else {
  3227. $cell = $this->_buildEmptyDisplay(
  3228. 'right ' . $class, $condition_field, $meta, ''
  3229. );
  3230. }
  3231. return $cell;
  3232. } // end of the '_getDataCellForNumericColumns()' function
  3233. /**
  3234. * Get data cell for geometry type fields
  3235. *
  3236. * @param string $column the relevant column in data row
  3237. * @param string $class the html class for column
  3238. * @param object $meta the meta-information about
  3239. * this field
  3240. * @param array $map the list of relations
  3241. * @param array $_url_params the parameters for generate url
  3242. * @param boolean $condition_field the column should highlighted
  3243. * or not
  3244. * @param object|string $transformation_plugin the name of transformation
  3245. * function
  3246. * @param string $default_function the default transformation
  3247. * function
  3248. * @param string $transform_options the transformation parameters
  3249. * @param array $analyzed_sql_results the analyzed query
  3250. *
  3251. * @return string $cell the prepared data cell, html content
  3252. *
  3253. * @access private
  3254. *
  3255. * @see _getTableBody()
  3256. */
  3257. private function _getDataCellForGeometryColumns(
  3258. $column, $class, $meta, $map, $_url_params, $condition_field,
  3259. $transformation_plugin, $default_function, $transform_options,
  3260. $analyzed_sql_results
  3261. ) {
  3262. if (! isset($column) || is_null($column)) {
  3263. $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
  3264. return $cell;
  3265. }
  3266. if ($column == '') {
  3267. $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
  3268. return $cell;
  3269. }
  3270. // Display as [GEOMETRY - (size)]
  3271. if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_GEOM) {
  3272. $geometry_text = $this->_handleNonPrintableContents(
  3273. strtoupper(self::GEOMETRY_FIELD), $column, $transformation_plugin,
  3274. $transform_options, $default_function, $meta, $_url_params
  3275. );
  3276. $cell = $this->_buildValueDisplay(
  3277. $class, $condition_field, $geometry_text
  3278. );
  3279. return $cell;
  3280. }
  3281. if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_WKT) {
  3282. // Prepare in Well Known Text(WKT) format.
  3283. $where_comparison = ' = ' . $column;
  3284. // Convert to WKT format
  3285. $wktval = PMA_Util::asWKT($column);
  3286. list(
  3287. $is_field_truncated,
  3288. $wktval,
  3289. // skip 3rd param
  3290. ) = $this->_getPartialText($wktval);
  3291. $cell = $this->_getRowData(
  3292. $class, $condition_field, $analyzed_sql_results, $meta, $map,
  3293. $wktval, $transformation_plugin, $default_function, '',
  3294. $where_comparison, $transform_options,
  3295. $is_field_truncated, ''
  3296. );
  3297. return $cell;
  3298. }
  3299. // Prepare in Well Known Binary (WKB) format.
  3300. if ($_SESSION['tmpval']['display_binary']) {
  3301. $where_comparison = ' = ' . $column;
  3302. $wkbval = substr(bin2hex($column), 8);
  3303. list(
  3304. $is_field_truncated,
  3305. $wkbval,
  3306. // skip 3rd param
  3307. ) = $this->_getPartialText($wkbval);
  3308. $cell = $this->_getRowData(
  3309. $class, $condition_field,
  3310. $analyzed_sql_results, $meta, $map, $wkbval,
  3311. $transformation_plugin, $default_function, '',
  3312. $where_comparison, $transform_options,
  3313. $is_field_truncated, ''
  3314. );
  3315. return $cell;
  3316. }
  3317. $wkbval = $this->_handleNonPrintableContents(
  3318. self::BINARY_FIELD, $column, $transformation_plugin,
  3319. $transform_options, $default_function, $meta,
  3320. $_url_params
  3321. );
  3322. $cell = $this->_buildValueDisplay(
  3323. $class, $condition_field, $wkbval
  3324. );
  3325. return $cell;
  3326. } // end of the '_getDataCellForGeometryColumns()' function
  3327. /**
  3328. * Get data cell for non numeric type fields
  3329. *
  3330. * @param string $column the relevant column in data row
  3331. * @param string $class the html class for column
  3332. * @param object $meta the meta-information about
  3333. * the field
  3334. * @param array $map the list of relations
  3335. * @param array $_url_params the parameters for generate
  3336. * url
  3337. * @param boolean $condition_field the column should highlighted
  3338. * or not
  3339. * @param object|string $transformation_plugin the name of transformation
  3340. * function
  3341. * @param string $default_function the default transformation
  3342. * function
  3343. * @param string $transform_options the transformation parameters
  3344. * @param boolean $is_field_truncated is data truncated due to
  3345. * LimitChars
  3346. * @param array $analyzed_sql_results the analyzed query
  3347. * @param integer &$dt_result the link id associated to
  3348. * the query which results
  3349. * have to be displayed
  3350. * @param integer $col_index the column index
  3351. *
  3352. * @return string $cell the prepared data cell, html content
  3353. *
  3354. * @access private
  3355. *
  3356. * @see _getTableBody()
  3357. */
  3358. private function _getDataCellForNonNumericColumns(
  3359. $column, $class, $meta, $map, $_url_params, $condition_field,
  3360. $transformation_plugin, $default_function, $transform_options,
  3361. $is_field_truncated, $analyzed_sql_results, &$dt_result, $col_index
  3362. ) {
  3363. $original_length = 0;
  3364. $is_analyse = $this->__get('is_analyse');
  3365. $field_flags = $GLOBALS['dbi']->fieldFlags($dt_result, $col_index);
  3366. $bIsText = gettype($transformation_plugin) === 'object'
  3367. && strpos($transformation_plugin->getMIMEtype(), 'Text')
  3368. === false;
  3369. // disable inline grid editing
  3370. // if binary fields are protected
  3371. // or transformation plugin is of non text type
  3372. // such as image
  3373. if ((stristr($field_flags, self::BINARY_FIELD)
  3374. && ($GLOBALS['cfg']['ProtectBinary'] === 'all'
  3375. || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob'
  3376. && !stristr($meta->type, self::BLOB_FIELD))
  3377. || ($GLOBALS['cfg']['ProtectBinary'] === 'blob'
  3378. && stristr($meta->type, self::BLOB_FIELD))))
  3379. || $bIsText
  3380. ) {
  3381. $class = str_replace('grid_edit', '', $class);
  3382. }
  3383. if (! isset($column) || is_null($column)) {
  3384. $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
  3385. return $cell;
  3386. }
  3387. if ($column == '') {
  3388. $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
  3389. return $cell;
  3390. }
  3391. // Cut all fields to $GLOBALS['cfg']['LimitChars']
  3392. // (unless it's a link-type transformation or binary)
  3393. if (!(gettype($transformation_plugin) === "object"
  3394. && strpos($transformation_plugin->getName(), 'Link') !== false)
  3395. && !stristr($field_flags, self::BINARY_FIELD)
  3396. ) {
  3397. list(
  3398. $is_field_truncated,
  3399. $column,
  3400. $original_length
  3401. ) = $this->_getPartialText($column);
  3402. }
  3403. $formatted = false;
  3404. if (isset($meta->_type) && $meta->_type === MYSQLI_TYPE_BIT) {
  3405. $column = PMA_Util::printableBitValue(
  3406. $column, $meta->length
  3407. );
  3408. // some results of PROCEDURE ANALYSE() are reported as
  3409. // being BINARY but they are quite readable,
  3410. // so don't treat them as BINARY
  3411. } elseif (stristr($field_flags, self::BINARY_FIELD)
  3412. && !(isset($is_analyse) && $is_analyse)
  3413. ) {
  3414. // we show the BINARY or BLOB message and field's size
  3415. // (or maybe use a transformation)
  3416. $binary_or_blob = self::BLOB_FIELD;
  3417. if ($meta->type === self::STRING_FIELD) {
  3418. $binary_or_blob = self::BINARY_FIELD;
  3419. }
  3420. $column = $this->_handleNonPrintableContents(
  3421. $binary_or_blob, $column, $transformation_plugin,
  3422. $transform_options, $default_function,
  3423. $meta, $_url_params, $is_field_truncated
  3424. );
  3425. $class = $this->_addClass(
  3426. $class, $condition_field, $meta, '',
  3427. $is_field_truncated, $transformation_plugin, $default_function
  3428. );
  3429. $result = strip_tags($column);
  3430. // disable inline grid editing
  3431. // if binary or blob data is not shown
  3432. if (stristr($result, $binary_or_blob)) {
  3433. $class = str_replace('grid_edit', '', $class);
  3434. }
  3435. $formatted = true;
  3436. }
  3437. if ($formatted) {
  3438. $cell = $this->_buildValueDisplay(
  3439. $class, $condition_field, $column
  3440. );
  3441. return $cell;
  3442. }
  3443. // transform functions may enable no-wrapping:
  3444. $function_nowrap = 'applyTransformationNoWrap';
  3445. $bool_nowrap = (($default_function != $transformation_plugin)
  3446. && function_exists($transformation_plugin->$function_nowrap()))
  3447. ? $transformation_plugin->$function_nowrap($transform_options)
  3448. : false;
  3449. // do not wrap if date field type
  3450. $nowrap = (preg_match('@DATE|TIME@i', $meta->type)
  3451. || $bool_nowrap) ? ' nowrap' : '';
  3452. $where_comparison = ' = \''
  3453. . PMA_Util::sqlAddSlashes($column)
  3454. . '\'';
  3455. $cell = $this->_getRowData(
  3456. $class, $condition_field,
  3457. $analyzed_sql_results, $meta, $map, $column,
  3458. $transformation_plugin, $default_function, $nowrap,
  3459. $where_comparison, $transform_options,
  3460. $is_field_truncated, $original_length
  3461. );
  3462. return $cell;
  3463. } // end of the '_getDataCellForNonNumericColumns()' function
  3464. /**
  3465. * Checks the posted options for viewing query results
  3466. * and sets appropriate values in the session.
  3467. *
  3468. * @todo make maximum remembered queries configurable
  3469. * @todo move/split into SQL class!?
  3470. * @todo currently this is called twice unnecessary
  3471. * @todo ignore LIMIT and ORDER in query!?
  3472. *
  3473. * @return void
  3474. *
  3475. * @access public
  3476. *
  3477. * @see sql.php file
  3478. */
  3479. public function setConfigParamsForDisplayTable()
  3480. {
  3481. $sql_md5 = md5($this->__get('sql_query'));
  3482. $query = array();
  3483. if (isset($_SESSION['tmpval']['query'][$sql_md5])) {
  3484. $query = $_SESSION['tmpval']['query'][$sql_md5];
  3485. }
  3486. $query['sql'] = $this->__get('sql_query');
  3487. if (empty($query['repeat_cells'])) {
  3488. $query['repeat_cells'] = $GLOBALS['cfg']['RepeatCells'];
  3489. }
  3490. // as this is a form value, the type is always string so we cannot
  3491. // use PMA_isValid($_REQUEST['session_max_rows'], 'integer')
  3492. if (PMA_isValid($_REQUEST['session_max_rows'], 'numeric')) {
  3493. $query['max_rows'] = (int)$_REQUEST['session_max_rows'];
  3494. unset($_REQUEST['session_max_rows']);
  3495. } elseif ($_REQUEST['session_max_rows'] == self::ALL_ROWS) {
  3496. $query['max_rows'] = self::ALL_ROWS;
  3497. unset($_REQUEST['session_max_rows']);
  3498. } elseif (empty($query['max_rows'])) {
  3499. $query['max_rows'] = $GLOBALS['cfg']['MaxRows'];
  3500. }
  3501. if (PMA_isValid($_REQUEST['pos'], 'numeric')) {
  3502. $query['pos'] = $_REQUEST['pos'];
  3503. unset($_REQUEST['pos']);
  3504. } elseif (empty($query['pos'])) {
  3505. $query['pos'] = 0;
  3506. }
  3507. if (PMA_isValid(
  3508. $_REQUEST['pftext'],
  3509. array(
  3510. self::DISPLAY_PARTIAL_TEXT, self::DISPLAY_FULL_TEXT
  3511. )
  3512. )
  3513. ) {
  3514. $query['pftext'] = $_REQUEST['pftext'];
  3515. unset($_REQUEST['pftext']);
  3516. } elseif (empty($query['pftext'])) {
  3517. $query['pftext'] = self::DISPLAY_PARTIAL_TEXT;
  3518. }
  3519. if (PMA_isValid(
  3520. $_REQUEST['relational_display'],
  3521. array(
  3522. self::RELATIONAL_KEY, self::RELATIONAL_DISPLAY_COLUMN
  3523. )
  3524. )
  3525. ) {
  3526. $query['relational_display'] = $_REQUEST['relational_display'];
  3527. unset($_REQUEST['relational_display']);
  3528. } elseif (empty($query['relational_display'])) {
  3529. // The current session value has priority over a
  3530. // change via Settings; this change will be apparent
  3531. // starting from the next session
  3532. $query['relational_display'] = $GLOBALS['cfg']['RelationalDisplay'];
  3533. }
  3534. if (PMA_isValid(
  3535. $_REQUEST['geoOption'],
  3536. array(
  3537. self::GEOMETRY_DISP_WKT, self::GEOMETRY_DISP_WKB,
  3538. self::GEOMETRY_DISP_GEOM
  3539. )
  3540. )
  3541. ) {
  3542. $query['geoOption'] = $_REQUEST['geoOption'];
  3543. unset($_REQUEST['geoOption']);
  3544. } elseif (empty($query['geoOption'])) {
  3545. $query['geoOption'] = self::GEOMETRY_DISP_GEOM;
  3546. }
  3547. if (isset($_REQUEST['display_binary'])) {
  3548. $query['display_binary'] = true;
  3549. unset($_REQUEST['display_binary']);
  3550. } elseif (isset($_REQUEST['display_options_form'])) {
  3551. // we know that the checkbox was unchecked
  3552. unset($query['display_binary']);
  3553. } elseif (isset($_REQUEST['full_text_button'])) {
  3554. // do nothing to keep the value that is there in the session
  3555. } else {
  3556. // selected by default because some operations like OPTIMIZE TABLE
  3557. // and all queries involving functions return "binary" contents,
  3558. // according to low-level field flags
  3559. $query['display_binary'] = true;
  3560. }
  3561. if (isset($_REQUEST['display_blob'])) {
  3562. $query['display_blob'] = true;
  3563. unset($_REQUEST['display_blob']);
  3564. } elseif (isset($_REQUEST['display_options_form'])) {
  3565. // we know that the checkbox was unchecked
  3566. unset($query['display_blob']);
  3567. }
  3568. if (isset($_REQUEST['hide_transformation'])) {
  3569. $query['hide_transformation'] = true;
  3570. unset($_REQUEST['hide_transformation']);
  3571. } elseif (isset($_REQUEST['display_options_form'])) {
  3572. // we know that the checkbox was unchecked
  3573. unset($query['hide_transformation']);
  3574. }
  3575. // move current query to the last position, to be removed last
  3576. // so only least executed query will be removed if maximum remembered
  3577. // queries limit is reached
  3578. unset($_SESSION['tmpval']['query'][$sql_md5]);
  3579. $_SESSION['tmpval']['query'][$sql_md5] = $query;
  3580. // do not exceed a maximum number of queries to remember
  3581. if (count($_SESSION['tmpval']['query']) > 10) {
  3582. array_shift($_SESSION['tmpval']['query']);
  3583. //echo 'deleting one element ...';
  3584. }
  3585. // populate query configuration
  3586. $_SESSION['tmpval']['pftext']
  3587. = $query['pftext'];
  3588. $_SESSION['tmpval']['relational_display']
  3589. = $query['relational_display'];
  3590. $_SESSION['tmpval']['geoOption']
  3591. = $query['geoOption'];
  3592. $_SESSION['tmpval']['display_binary'] = isset(
  3593. $query['display_binary']
  3594. );
  3595. $_SESSION['tmpval']['display_blob'] = isset(
  3596. $query['display_blob']
  3597. );
  3598. $_SESSION['tmpval']['hide_transformation'] = isset(
  3599. $query['hide_transformation']
  3600. );
  3601. $_SESSION['tmpval']['pos']
  3602. = $query['pos'];
  3603. $_SESSION['tmpval']['max_rows']
  3604. = $query['max_rows'];
  3605. $_SESSION['tmpval']['repeat_cells']
  3606. = $query['repeat_cells'];
  3607. }
  3608. /**
  3609. * Prepare a table of results returned by a SQL query.
  3610. *
  3611. * @param integer &$dt_result the link id associated to the query
  3612. * which results have to be displayed
  3613. * @param array &$displayParts the parts to display
  3614. * @param array $analyzed_sql_results analyzed sql results
  3615. * @param boolean $is_limited_display With limited operations or not
  3616. *
  3617. * @return string $table_html Generated HTML content for resulted table
  3618. *
  3619. * @access public
  3620. *
  3621. * @see sql.php file
  3622. */
  3623. public function getTable(
  3624. &$dt_result, &$displayParts, $analyzed_sql_results,
  3625. $is_limited_display = false
  3626. ) {
  3627. /**
  3628. * The statement this table is built for.
  3629. * @var SqlParser\Statements\SelectStatement
  3630. */
  3631. $statement = $analyzed_sql_results['statement'];
  3632. $table_html = '';
  3633. // Following variable are needed for use in isset/empty or
  3634. // use with array indexes/safe use in foreach
  3635. $fields_meta = $this->__get('fields_meta');
  3636. $showtable = $this->__get('showtable');
  3637. $printview = $this->__get('printview');
  3638. // why was this called here? (already called from sql.php)
  3639. //$this->setConfigParamsForDisplayTable();
  3640. /**
  3641. * @todo move this to a central place
  3642. * @todo for other future table types
  3643. */
  3644. $is_innodb = (isset($showtable['Type'])
  3645. && $showtable['Type'] == self::TABLE_TYPE_INNO_DB);
  3646. if ($is_innodb
  3647. && PMA_isJustBrowsing($analyzed_sql_results, true)
  3648. ) {
  3649. // "j u s t b r o w s i n g"
  3650. $pre_count = '~';
  3651. $after_count = PMA_Util::showHint(
  3652. PMA_sanitize(
  3653. __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc].')
  3654. )
  3655. );
  3656. } else {
  3657. $pre_count = '';
  3658. $after_count = '';
  3659. }
  3660. // 1. ----- Prepares the work -----
  3661. // 1.1 Gets the information about which functionalities should be
  3662. // displayed
  3663. $total = '';
  3664. $displayParts = $this->_setDisplayParts($displayParts, $total);
  3665. // 1.2 Defines offsets for the next and previous pages
  3666. if ($displayParts['nav_bar'] == '1') {
  3667. list($pos_next, $pos_prev) = $this->_getOffsets();
  3668. } // end if
  3669. // 1.3 Extract sorting expressions.
  3670. // we need $sort_expression and $sort_expression_nodirection
  3671. // even if there are many table references
  3672. $sort_expression = array();
  3673. $sort_expression_nodirection = array();
  3674. $sort_direction = array();
  3675. if (!empty($statement->order)) {
  3676. foreach ($statement->order as $o) {
  3677. $sort_expression[] = $o->expr->expr . ' ' . $o->type;
  3678. $sort_expression_nodirection[] = $o->expr->expr;
  3679. $sort_direction[] = $o->type;
  3680. }
  3681. } else {
  3682. $sort_expression[] = '';
  3683. $sort_expression_nodirection[] = '';
  3684. $sort_direction[] = '';
  3685. }
  3686. $number_of_columns = count($sort_expression_nodirection);
  3687. // 1.4 Prepares display of first and last value of the sorted column
  3688. $sorted_column_message = '';
  3689. for ( $i = 0; $i < $number_of_columns; $i++ ) {
  3690. $sorted_column_message .= $this->_getSortedColumnMessage(
  3691. $dt_result, $sort_expression_nodirection[$i]
  3692. );
  3693. }
  3694. // 2. ----- Prepare to display the top of the page -----
  3695. // 2.1 Prepares a messages with position information
  3696. if (($displayParts['nav_bar'] == '1') && isset($pos_next)) {
  3697. $message = $this->_setMessageInformation(
  3698. $sorted_column_message,
  3699. $analyzed_sql_results,
  3700. $total,
  3701. $pos_next,
  3702. $pre_count,
  3703. $after_count
  3704. );
  3705. $table_html .= PMA_Util::getMessage(
  3706. $message, $this->__get('sql_query'), 'success'
  3707. );
  3708. } elseif (! isset($printview) || ($printview != '1')) {
  3709. $table_html .= PMA_Util::getMessage(
  3710. __('Your SQL query has been executed successfully.'),
  3711. $this->__get('sql_query'), 'success'
  3712. );
  3713. }
  3714. // 2.3 Prepare the navigation bars
  3715. if (!/*overload*/mb_strlen($this->__get('table'))) {
  3716. if ($analyzed_sql_results['querytype'] == 'SELECT') {
  3717. // table does not always contain a real table name,
  3718. // for example in MySQL 5.0.x, the query SHOW STATUS
  3719. // returns STATUS as a table name
  3720. $this->__set('table', $fields_meta[0]->table);
  3721. } else {
  3722. $this->__set('table', '');
  3723. }
  3724. }
  3725. if (($displayParts['nav_bar'] == '1') && (empty($statement->limit))) {
  3726. $table_html .= $this->_getPlacedTableNavigations(
  3727. $pos_next, $pos_prev, self::PLACE_TOP_DIRECTION_DROPDOWN,
  3728. $is_innodb
  3729. );
  3730. } elseif (! isset($printview) || ($printview != '1')) {
  3731. $table_html .= "\n" . '<br /><br />' . "\n";
  3732. }
  3733. // 2b ----- Get field references from Database -----
  3734. // (see the 'relation' configuration variable)
  3735. // initialize map
  3736. $map = array();
  3737. $target = array();
  3738. if (!empty($statement->from)) {
  3739. foreach ($statement->from as $field) {
  3740. if (!empty($field->table)) {
  3741. $target[] = $field->table;
  3742. }
  3743. }
  3744. }
  3745. if (/*overload*/mb_strlen($this->__get('table'))) {
  3746. // This method set the values for $map array
  3747. $this->_setParamForLinkForeignKeyRelatedTables($map);
  3748. // Coming from 'Distinct values' action of structure page
  3749. // We manipulate relations mechanism to show a link to related rows.
  3750. if ($this->__get('is_browse_distinct')) {
  3751. $map[$fields_meta[1]->name] = array(
  3752. $this->__get('table'),
  3753. $fields_meta[1]->name,
  3754. '',
  3755. $this->__get('db')
  3756. );
  3757. }
  3758. } // end if
  3759. // end 2b
  3760. // 3. ----- Prepare the results table -----
  3761. $table_html .= $this->_getTableHeaders(
  3762. $displayParts,
  3763. $analyzed_sql_results,
  3764. $sort_expression,
  3765. $sort_expression_nodirection,
  3766. $sort_direction,
  3767. $is_limited_display
  3768. );
  3769. $table_html .= '<tbody>' . "\n";
  3770. $table_html .= $this->_getTableBody(
  3771. $dt_result,
  3772. $displayParts,
  3773. $map,
  3774. $analyzed_sql_results,
  3775. $is_limited_display
  3776. );
  3777. $this->__set('display_params', null);
  3778. $table_html .= '</tbody>' . "\n" . '</table>';
  3779. // 4. ----- Prepares the link for multi-fields edit and delete
  3780. if ($displayParts['del_lnk'] == self::DELETE_ROW
  3781. && $displayParts['del_lnk'] != self::KILL_PROCESS
  3782. ) {
  3783. $table_html .= $this->_getMultiRowOperationLinks(
  3784. $dt_result,
  3785. $analyzed_sql_results,
  3786. $displayParts['del_lnk']
  3787. );
  3788. }
  3789. // 5. ----- Get the navigation bar at the bottom if required -----
  3790. if (($displayParts['nav_bar'] == '1') && empty($statement->limit)) {
  3791. $table_html .= $this->_getPlacedTableNavigations(
  3792. $pos_next, $pos_prev, self::PLACE_BOTTOM_DIRECTION_DROPDOWN,
  3793. $is_innodb
  3794. );
  3795. } elseif (! isset($printview) || ($printview != '1')) {
  3796. $table_html .= "\n" . '<br /><br />' . "\n";
  3797. }
  3798. // 6. ----- Prepare "Query results operations"
  3799. if ((! isset($printview) || ($printview != '1')) && ! $is_limited_display) {
  3800. $table_html .= $this->_getResultsOperations(
  3801. $displayParts, $analyzed_sql_results
  3802. );
  3803. }
  3804. return $table_html;
  3805. } // end of the 'getTable()' function
  3806. /**
  3807. * Get offsets for next page and previous page
  3808. *
  3809. * @return array array with two elements - $pos_next, $pos_prev
  3810. *
  3811. * @access private
  3812. *
  3813. * @see getTable()
  3814. */
  3815. private function _getOffsets()
  3816. {
  3817. if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
  3818. $pos_next = 0;
  3819. $pos_prev = 0;
  3820. } else {
  3821. $pos_next = $_SESSION['tmpval']['pos']
  3822. + $_SESSION['tmpval']['max_rows'];
  3823. $pos_prev = $_SESSION['tmpval']['pos']
  3824. - $_SESSION['tmpval']['max_rows'];
  3825. if ($pos_prev < 0) {
  3826. $pos_prev = 0;
  3827. }
  3828. }
  3829. return array($pos_next, $pos_prev);
  3830. } // end of the '_getOffsets()' function
  3831. /**
  3832. * Prepare sorted column message
  3833. *
  3834. * @param integer &$dt_result the link id associated to the
  3835. * query which results have to
  3836. * be displayed
  3837. * @param string $sort_expression_nodirection sort expression without direction
  3838. *
  3839. * @return string html content
  3840. * null if not found sorted column
  3841. *
  3842. * @access private
  3843. *
  3844. * @see getTable()
  3845. */
  3846. private function _getSortedColumnMessage(
  3847. &$dt_result, $sort_expression_nodirection
  3848. ) {
  3849. $fields_meta = $this->__get('fields_meta'); // To use array indexes
  3850. if (empty($sort_expression_nodirection)) {
  3851. return null;
  3852. }
  3853. if (/*overload*/mb_strpos($sort_expression_nodirection, '.') === false) {
  3854. $sort_table = $this->__get('table');
  3855. $sort_column = $sort_expression_nodirection;
  3856. } else {
  3857. list($sort_table, $sort_column)
  3858. = explode('.', $sort_expression_nodirection);
  3859. }
  3860. $sort_table = PMA_Util::unQuote($sort_table);
  3861. $sort_column = PMA_Util::unQuote($sort_column);
  3862. // find the sorted column index in row result
  3863. // (this might be a multi-table query)
  3864. $sorted_column_index = false;
  3865. foreach ($fields_meta as $key => $meta) {
  3866. if (($meta->table == $sort_table) && ($meta->name == $sort_column)) {
  3867. $sorted_column_index = $key;
  3868. break;
  3869. }
  3870. }
  3871. if ($sorted_column_index === false) {
  3872. return null;
  3873. }
  3874. // fetch first row of the result set
  3875. $row = $GLOBALS['dbi']->fetchRow($dt_result);
  3876. // initializing default arguments
  3877. $default_function = 'PMA_mimeDefaultFunction';
  3878. $transformation_plugin = $default_function;
  3879. $transform_options = array();
  3880. // check for non printable sorted row data
  3881. $meta = $fields_meta[$sorted_column_index];
  3882. if (stristr($meta->type, self::BLOB_FIELD)
  3883. || ($meta->type == self::GEOMETRY_FIELD)
  3884. ) {
  3885. $column_for_first_row = $this->_handleNonPrintableContents(
  3886. $meta->type, $row[$sorted_column_index],
  3887. $transformation_plugin, $transform_options,
  3888. $default_function, $meta
  3889. );
  3890. } else {
  3891. $column_for_first_row = $row[$sorted_column_index];
  3892. }
  3893. $column_for_first_row = /*overload*/mb_strtoupper(
  3894. /*overload*/mb_substr(
  3895. $column_for_first_row, 0, $GLOBALS['cfg']['LimitChars']
  3896. ) . '...'
  3897. );
  3898. // fetch last row of the result set
  3899. $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1);
  3900. $row = $GLOBALS['dbi']->fetchRow($dt_result);
  3901. // check for non printable sorted row data
  3902. $meta = $fields_meta[$sorted_column_index];
  3903. if (stristr($meta->type, self::BLOB_FIELD)
  3904. || ($meta->type == self::GEOMETRY_FIELD)
  3905. ) {
  3906. $column_for_last_row = $this->_handleNonPrintableContents(
  3907. $meta->type, $row[$sorted_column_index],
  3908. $transformation_plugin, $transform_options,
  3909. $default_function, $meta
  3910. );
  3911. } else {
  3912. $column_for_last_row = $row[$sorted_column_index];
  3913. }
  3914. $column_for_last_row = /*overload*/mb_strtoupper(
  3915. /*overload*/mb_substr(
  3916. $column_for_last_row, 0, $GLOBALS['cfg']['LimitChars']
  3917. ) . '...'
  3918. );
  3919. // reset to first row for the loop in _getTableBody()
  3920. $GLOBALS['dbi']->dataSeek($dt_result, 0);
  3921. // we could also use here $sort_expression_nodirection
  3922. return ' [' . htmlspecialchars($sort_column)
  3923. . ': <strong>' . htmlspecialchars($column_for_first_row) . ' - '
  3924. . htmlspecialchars($column_for_last_row) . '</strong>]';
  3925. } // end of the '_getSortedColumnMessage()' function
  3926. /**
  3927. * Set the content that needs to be shown in message
  3928. *
  3929. * @param string $sorted_column_message the message for sorted column
  3930. * @param array $analyzed_sql_results the analyzed query
  3931. * @param integer $total the total number of rows returned by
  3932. * the SQL query without any
  3933. * programmatically appended LIMIT clause
  3934. * @param integer $pos_next the offset for next page
  3935. * @param string $pre_count the string renders before row count
  3936. * @param string $after_count the string renders after row count
  3937. *
  3938. * @return PMA_Message $message an object of PMA_Message
  3939. *
  3940. * @access private
  3941. *
  3942. * @see getTable()
  3943. */
  3944. private function _setMessageInformation(
  3945. $sorted_column_message, $analyzed_sql_results, $total,
  3946. $pos_next, $pre_count, $after_count
  3947. ) {
  3948. $unlim_num_rows = $this->__get('unlim_num_rows'); // To use in isset()
  3949. if (!empty($analyzed_sql_results['statement']->limit)) {
  3950. $first_shown_rec = $analyzed_sql_results['statement']->limit->offset;
  3951. $row_count = $analyzed_sql_results['statement']->limit->rowCount;
  3952. if ($row_count < $total) {
  3953. $last_shown_rec = $first_shown_rec + $row_count - 1;
  3954. } else {
  3955. $last_shown_rec = $first_shown_rec + $total - 1;
  3956. }
  3957. } elseif (($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS)
  3958. || ($pos_next > $total)
  3959. ) {
  3960. $first_shown_rec = $_SESSION['tmpval']['pos'];
  3961. $last_shown_rec = $total - 1;
  3962. } else {
  3963. $first_shown_rec = $_SESSION['tmpval']['pos'];
  3964. $last_shown_rec = $pos_next - 1;
  3965. }
  3966. $table = new PMA_Table($this->__get('table'), $this->__get('db'));
  3967. if ($table->isView()
  3968. && ($total == $GLOBALS['cfg']['MaxExactCountViews'])
  3969. ) {
  3970. $message = PMA_Message::notice(
  3971. __(
  3972. 'This view has at least this number of rows. '
  3973. . 'Please refer to %sdocumentation%s.'
  3974. )
  3975. );
  3976. $message->addParam('[doc@cfg_MaxExactCount]');
  3977. $message->addParam('[/doc]');
  3978. $message_view_warning = PMA_Util::showHint($message);
  3979. } else {
  3980. $message_view_warning = false;
  3981. }
  3982. $message = PMA_Message::success(__('Showing rows %1s - %2s'));
  3983. $message->addParam($first_shown_rec);
  3984. if ($message_view_warning !== false) {
  3985. $message->addParam('... ' . $message_view_warning, false);
  3986. } else {
  3987. $message->addParam($last_shown_rec);
  3988. }
  3989. $message->addMessage('(');
  3990. if ($message_view_warning === false) {
  3991. if (isset($unlim_num_rows) && ($unlim_num_rows != $total)) {
  3992. $message_total = PMA_Message::notice(
  3993. $pre_count . __('%1$d total, %2$d in query')
  3994. );
  3995. $message_total->addParam($total);
  3996. $message_total->addParam($unlim_num_rows);
  3997. } else {
  3998. $message_total = PMA_Message::notice($pre_count . __('%d total'));
  3999. $message_total->addParam($total);
  4000. }
  4001. if (!empty($after_count)) {
  4002. $message_total->addMessage($after_count);
  4003. }
  4004. $message->addMessage($message_total, '');
  4005. $message->addMessage(', ', '');
  4006. }
  4007. $message_qt = PMA_Message::notice(__('Query took %01.4f seconds.') . ')');
  4008. $message_qt->addParam($this->__get('querytime'));
  4009. $message->addMessage($message_qt, '');
  4010. if (! is_null($sorted_column_message)) {
  4011. $message->addMessage($sorted_column_message, '');
  4012. }
  4013. return $message;
  4014. } // end of the '_setMessageInformation()' function
  4015. /**
  4016. * Set the value of $map array for linking foreign key related tables
  4017. *
  4018. * @param array &$map the list of relations
  4019. *
  4020. * @return void
  4021. *
  4022. * @access private
  4023. *
  4024. * @see getTable()
  4025. */
  4026. private function _setParamForLinkForeignKeyRelatedTables(&$map)
  4027. {
  4028. // To be able to later display a link to the related table,
  4029. // we verify both types of relations: either those that are
  4030. // native foreign keys or those defined in the phpMyAdmin
  4031. // configuration storage. If no PMA storage, we won't be able
  4032. // to use the "column to display" notion (for example show
  4033. // the name related to a numeric id).
  4034. $exist_rel = PMA_getForeigners(
  4035. $this->__get('db'), $this->__get('table'), '', self::POSITION_BOTH
  4036. );
  4037. if (! empty($exist_rel)) {
  4038. foreach ($exist_rel as $master_field => $rel) {
  4039. if ($master_field != 'foreign_keys_data') {
  4040. $display_field = PMA_getDisplayField(
  4041. $rel['foreign_db'], $rel['foreign_table']
  4042. );
  4043. $map[$master_field] = array(
  4044. $rel['foreign_table'],
  4045. $rel['foreign_field'],
  4046. $display_field,
  4047. $rel['foreign_db']
  4048. );
  4049. } else {
  4050. foreach ($rel as $key => $one_key) {
  4051. foreach ($one_key['index_list'] as $index => $one_field) {
  4052. $display_field = PMA_getDisplayField(
  4053. isset($one_key['ref_db_name'])
  4054. ? $one_key['ref_db_name']
  4055. : $GLOBALS['db'],
  4056. $one_key['ref_table_name']
  4057. );
  4058. $map[$one_field] = array(
  4059. $one_key['ref_table_name'],
  4060. $one_key['ref_index_list'][$index],
  4061. $display_field,
  4062. isset($one_key['ref_db_name'])
  4063. ? $one_key['ref_db_name']
  4064. : $GLOBALS['db']
  4065. );
  4066. }
  4067. }
  4068. }
  4069. } // end while
  4070. } // end if
  4071. } // end of the '_setParamForLinkForeignKeyRelatedTables()' function
  4072. /**
  4073. * Prepare multi field edit/delete links
  4074. *
  4075. * @param integer &$dt_result the link id associated to the query which results have to be displayed
  4076. * which results have to be displayed
  4077. * @param array $analyzed_sql_results analyzed sql results
  4078. * @param string $del_link the display element - 'del_link'
  4079. *
  4080. * @return string $links_html html content
  4081. *
  4082. * @access private
  4083. *
  4084. * @see getTable()
  4085. */
  4086. private function _getMultiRowOperationLinks(
  4087. &$dt_result, $analyzed_sql_results, $del_link
  4088. ) {
  4089. $links_html = '<div class="print_ignore" >';
  4090. $url_query = $this->__get('url_query');
  4091. $delete_text = ($del_link == self::DELETE_ROW) ? __('Delete') : __('Kill');
  4092. $links_html .= '<img class="selectallarrow" width="38" height="22"'
  4093. . ' src="' . $this->__get('pma_theme_image') . 'arrow_'
  4094. . $this->__get('text_dir') . '.png' . '"'
  4095. . ' alt="' . __('With selected:') . '" />';
  4096. $links_html .= '<input type="checkbox" '
  4097. . 'id="resultsForm_' . $this->__get('unique_id') . '_checkall" '
  4098. . 'class="checkall_box" title="' . __('Check all') . '" /> '
  4099. . '<label for="resultsForm_' . $this->__get('unique_id') . '_checkall">'
  4100. . __('Check all') . '</label> '
  4101. . '<i style="margin-left: 2em">' . __('With selected:') . '</i>' . "\n";
  4102. $links_html .= PMA_Util::getButtonOrImage(
  4103. 'submit_mult', 'mult_submit', 'submit_mult_change',
  4104. __('Edit'), 'b_edit.png', 'edit'
  4105. );
  4106. $links_html .= PMA_Util::getButtonOrImage(
  4107. 'submit_mult', 'mult_submit', 'submit_mult_copy',
  4108. __('Copy'), 'b_insrow.png', 'copy'
  4109. );
  4110. $links_html .= PMA_Util::getButtonOrImage(
  4111. 'submit_mult', 'mult_submit', 'submit_mult_delete',
  4112. $delete_text, 'b_drop.png', 'delete'
  4113. );
  4114. if ($analyzed_sql_results['querytype'] == 'SELECT') {
  4115. $links_html .= PMA_Util::getButtonOrImage(
  4116. 'submit_mult', 'mult_submit', 'submit_mult_export',
  4117. __('Export'), 'b_tblexport.png', 'export'
  4118. );
  4119. }
  4120. $links_html .= "</div>\n";
  4121. $links_html .= '<input type="hidden" name="sql_query"'
  4122. . ' value="' . htmlspecialchars($this->__get('sql_query')) . '" />'
  4123. . "\n";
  4124. if (! empty($url_query)) {
  4125. $links_html .= '<input type="hidden" name="url_query"'
  4126. . ' value="' . $url_query . '" />' . "\n";
  4127. }
  4128. // fetch last row of the result set
  4129. $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1);
  4130. $row = $GLOBALS['dbi']->fetchRow($dt_result);
  4131. // $clause_is_unique is needed by getTable() to generate the proper param
  4132. // in the multi-edit and multi-delete form
  4133. list($where_clause, $clause_is_unique, $condition_array)
  4134. = PMA_Util::getUniqueCondition(
  4135. $dt_result, // handle
  4136. $this->__get('fields_cnt'), // fields_cnt
  4137. $this->__get('fields_meta'), // fields_meta
  4138. $row, // row
  4139. false, // force_unique
  4140. false, // restrict_to_table
  4141. $analyzed_sql_results // analyzed_sql_results
  4142. );
  4143. unset($where_clause, $condition_array);
  4144. // reset to first row for the loop in _getTableBody()
  4145. $GLOBALS['dbi']->dataSeek($dt_result, 0);
  4146. $links_html .= '<input type="hidden" name="clause_is_unique"'
  4147. . ' value="' . $clause_is_unique . '" />' . "\n";
  4148. $links_html .= '</form>' . "\n";
  4149. return $links_html;
  4150. } // end of the '_getMultiRowOperationLinks()' function
  4151. /**
  4152. * Prepare table navigation bar at the top or bottom
  4153. *
  4154. * @param integer $pos_next the offset for the "next" page
  4155. * @param integer $pos_prev the offset for the "previous" page
  4156. * @param string $place the place to show navigation
  4157. * @param boolean $is_innodb whether its InnoDB or not
  4158. *
  4159. * @return string html content of navigation bar
  4160. *
  4161. * @access private
  4162. *
  4163. * @see _getTable()
  4164. */
  4165. private function _getPlacedTableNavigations(
  4166. $pos_next, $pos_prev, $place, $is_innodb
  4167. ) {
  4168. $navigation_html = '';
  4169. if ($place == self::PLACE_BOTTOM_DIRECTION_DROPDOWN) {
  4170. $navigation_html .= '<br />' . "\n";
  4171. }
  4172. $navigation_html .= $this->_getTableNavigation(
  4173. $pos_next, $pos_prev, $is_innodb
  4174. );
  4175. if ($place == self::PLACE_TOP_DIRECTION_DROPDOWN) {
  4176. $navigation_html .= "\n";
  4177. }
  4178. return $navigation_html;
  4179. } // end of the '_getPlacedTableNavigations()' function
  4180. /**
  4181. * Generates HTML to display the Create view in span tag
  4182. *
  4183. * @param array $analyzed_sql_results analyzed sql results
  4184. * @param string $url_query String with URL Parameters
  4185. *
  4186. * @return string
  4187. *
  4188. * @access private
  4189. *
  4190. * @see _getResultsOperations()
  4191. */
  4192. private function _getLinkForCreateView($analyzed_sql_results, $url_query)
  4193. {
  4194. $results_operations_html = '';
  4195. if (!PMA_DRIZZLE && empty($analyzed_sql_results['procedure'])) {
  4196. $ajax_class = ' ajax';
  4197. $results_operations_html .= '<span>'
  4198. . PMA_Util::linkOrButton(
  4199. 'view_create.php' . $url_query,
  4200. PMA_Util::getIcon(
  4201. 'b_view_add.png', __('Create view'), true
  4202. ),
  4203. array('class' => 'create_view' . $ajax_class), true, true, ''
  4204. )
  4205. . '</span>' . "\n";
  4206. }
  4207. return $results_operations_html;
  4208. }
  4209. /**
  4210. * Calls the _getResultsOperations with $only_view as true
  4211. *
  4212. * @param array $analyzed_sql_results analyzed sql results
  4213. *
  4214. * @return string
  4215. *
  4216. * @access public
  4217. *
  4218. */
  4219. public function getCreateViewQueryResultOp($analyzed_sql_results)
  4220. {
  4221. $results_operations_html = '';
  4222. //calling to _getResultOperations with a fake $displayParts
  4223. //and setting only_view parameter to be true to generate just view
  4224. $results_operations_html .= $this->_getResultsOperations(
  4225. array(),
  4226. $analyzed_sql_results,
  4227. true
  4228. );
  4229. return $results_operations_html;
  4230. }
  4231. /**
  4232. * Get printview links for results operations
  4233. *
  4234. * @return string $html
  4235. *
  4236. * @access private
  4237. */
  4238. private function _getPrintviewLinks()
  4239. {
  4240. $html = PMA_Util::linkOrButton(
  4241. '#',
  4242. PMA_Util::getIcon(
  4243. 'b_print.png', __('Print view'), true
  4244. ),
  4245. array('id' => 'printView'),
  4246. true,
  4247. true,
  4248. 'print_view'
  4249. );
  4250. return $html;
  4251. }
  4252. /**
  4253. * Get operations that are available on results.
  4254. *
  4255. * @param array $displayParts the parts to display
  4256. * @param array $analyzed_sql_results analyzed sql results
  4257. * @param boolean $only_view Whether to show only view
  4258. *
  4259. * @return string $results_operations_html html content
  4260. *
  4261. * @access private
  4262. *
  4263. * @see getTable()
  4264. */
  4265. private function _getResultsOperations(
  4266. $displayParts, $analyzed_sql_results, $only_view = false
  4267. ) {
  4268. global $printview;
  4269. $results_operations_html = '';
  4270. $fields_meta = $this->__get('fields_meta'); // To safe use in foreach
  4271. $header_shown = false;
  4272. $header = '<fieldset class="print_ignore" ><legend>' . __('Query results operations')
  4273. . '</legend>';
  4274. $_url_params = array(
  4275. 'db' => $this->__get('db'),
  4276. 'table' => $this->__get('table'),
  4277. 'printview' => '1',
  4278. 'sql_query' => $this->__get('sql_query'),
  4279. );
  4280. $url_query = PMA_URL_getCommon($_url_params);
  4281. if (!$header_shown) {
  4282. $results_operations_html .= $header;
  4283. $header_shown = true;
  4284. }
  4285. // if empty result set was produced we need to
  4286. // show only view and not other options
  4287. if ($only_view) {
  4288. $results_operations_html .= $this->_getLinkForCreateView(
  4289. $analyzed_sql_results, $url_query
  4290. );
  4291. if ($header_shown) {
  4292. $results_operations_html .= '</fieldset><br />';
  4293. }
  4294. return $results_operations_html;
  4295. }
  4296. // Displays "printable view" link if required
  4297. if ($displayParts['pview_lnk'] == '1') {
  4298. $results_operations_html .= $this->_getPrintviewLinks();
  4299. } // end displays "printable view"
  4300. // Export link
  4301. // (the url_query has extra parameters that won't be used to export)
  4302. // (the single_table parameter is used in display_export.inc.php
  4303. // to hide the SQL and the structure export dialogs)
  4304. // If the parser found a PROCEDURE clause
  4305. // (most probably PROCEDURE ANALYSE()) it makes no sense to
  4306. // display the Export link).
  4307. if (($analyzed_sql_results['querytype'] == self::QUERY_TYPE_SELECT)
  4308. && ! isset($printview)
  4309. && empty($analyzed_sql_results['procedure'])
  4310. ) {
  4311. if (count($analyzed_sql_results['select_tables']) == 1) {
  4312. $_url_params['single_table'] = 'true';
  4313. }
  4314. if (! $header_shown) {
  4315. $results_operations_html .= $header;
  4316. $header_shown = true;
  4317. }
  4318. $_url_params['unlim_num_rows'] = $this->__get('unlim_num_rows');
  4319. /**
  4320. * At this point we don't know the table name; this can happen
  4321. * for example with a query like
  4322. * SELECT bike_code FROM (SELECT bike_code FROM bikes) tmp
  4323. * As a workaround we set in the table parameter the name of the
  4324. * first table of this database, so that tbl_export.php and
  4325. * the script it calls do not fail
  4326. */
  4327. if (empty($_url_params['table']) && ! empty($_url_params['db'])) {
  4328. $_url_params['table'] = $GLOBALS['dbi']->fetchValue("SHOW TABLES");
  4329. /* No result (probably no database selected) */
  4330. if ($_url_params['table'] === false) {
  4331. unset($_url_params['table']);
  4332. }
  4333. }
  4334. $results_operations_html .= PMA_Util::linkOrButton(
  4335. 'tbl_export.php' . PMA_URL_getCommon($_url_params),
  4336. PMA_Util::getIcon(
  4337. 'b_tblexport.png', __('Export'), true
  4338. ),
  4339. '',
  4340. true,
  4341. true,
  4342. ''
  4343. )
  4344. . "\n";
  4345. // prepare chart
  4346. $results_operations_html .= PMA_Util::linkOrButton(
  4347. 'tbl_chart.php' . PMA_URL_getCommon($_url_params),
  4348. PMA_Util::getIcon(
  4349. 'b_chart.png', __('Display chart'), true
  4350. ),
  4351. '',
  4352. true,
  4353. true,
  4354. ''
  4355. )
  4356. . "\n";
  4357. // prepare GIS chart
  4358. $geometry_found = false;
  4359. // If at least one geometry field is found
  4360. foreach ($fields_meta as $meta) {
  4361. if ($meta->type == self::GEOMETRY_FIELD) {
  4362. $geometry_found = true;
  4363. break;
  4364. }
  4365. }
  4366. if ($geometry_found) {
  4367. $results_operations_html
  4368. .= PMA_Util::linkOrButton(
  4369. 'tbl_gis_visualization.php'
  4370. . PMA_URL_getCommon($_url_params),
  4371. PMA_Util::getIcon(
  4372. 'b_globe.gif', __('Visualize GIS data'), true
  4373. ),
  4374. '',
  4375. true,
  4376. true,
  4377. ''
  4378. )
  4379. . "\n";
  4380. }
  4381. }
  4382. // CREATE VIEW
  4383. /**
  4384. *
  4385. * @todo detect privileges to create a view
  4386. * (but see 2006-01-19 note in display_create_table.lib.php,
  4387. * I think we cannot detect db-specific privileges reliably)
  4388. * Note: we don't display a Create view link if we found a PROCEDURE clause
  4389. */
  4390. if (!$header_shown) {
  4391. $results_operations_html .= $header;
  4392. $header_shown = true;
  4393. }
  4394. $results_operations_html .= $this->_getLinkForCreateView(
  4395. $analyzed_sql_results, $url_query
  4396. );
  4397. if ($header_shown) {
  4398. $results_operations_html .= '</fieldset><br />';
  4399. }
  4400. return $results_operations_html;
  4401. } // end of the '_getResultsOperations()' function
  4402. /**
  4403. * Verifies what to do with non-printable contents (binary or BLOB)
  4404. * in Browse mode.
  4405. *
  4406. * @param string $category BLOB|BINARY|GEOMETRY
  4407. * @param string $content the binary content
  4408. * @param mixed $transformation_plugin transformation plugin.
  4409. * Can also be the default function:
  4410. * PMA_mimeDefaultFunction
  4411. * @param string $transform_options transformation parameters
  4412. * @param string $default_function default transformation function
  4413. * @param object $meta the meta-information about the field
  4414. * @param array $url_params parameters that should go to the
  4415. * download link
  4416. * @param boolean &$is_truncated the result is truncated or not
  4417. *
  4418. * @return mixed string or float
  4419. *
  4420. * @access private
  4421. *
  4422. * @see _getDataCellForGeometryColumns(),
  4423. * _getDataCellForNonNumericColumns(),
  4424. * _getSortedColumnMessage()
  4425. */
  4426. private function _handleNonPrintableContents(
  4427. $category, $content, $transformation_plugin, $transform_options,
  4428. $default_function, $meta, $url_params = array(), &$is_truncated = null
  4429. ) {
  4430. $is_truncated = false;
  4431. $result = '[' . $category;
  4432. if (isset($content)) {
  4433. $size = /*overload*/mb_strlen($content, '8bit');
  4434. $display_size = PMA_Util::formatByteDown($size, 3, 1);
  4435. $result .= ' - ' . $display_size[0] . ' ' . $display_size[1];
  4436. } else {
  4437. $result .= ' - NULL';
  4438. $size = 0;
  4439. }
  4440. $result .= ']';
  4441. // if we want to use a text transformation on a BLOB column
  4442. if (gettype($transformation_plugin) === "object") {
  4443. $posMimeOctetstream = strpos(
  4444. $transformation_plugin->getMIMESubtype(),
  4445. 'Octetstream'
  4446. );
  4447. $posMimeText = strpos($transformation_plugin->getMIMEtype(), 'Text');
  4448. if ($posMimeOctetstream
  4449. || $posMimeText !== false
  4450. ) {
  4451. // Applying Transformations on hex string of binary data
  4452. // seems more appropriate
  4453. $result = pack("H*", bin2hex($content));
  4454. }
  4455. }
  4456. if ($size <= 0) {
  4457. return($result);
  4458. }
  4459. if ($default_function != $transformation_plugin) {
  4460. $result = $transformation_plugin->applyTransformation(
  4461. $result,
  4462. $transform_options,
  4463. $meta
  4464. );
  4465. return($result);
  4466. }
  4467. $result = $default_function($result, array(), $meta);
  4468. if (($_SESSION['tmpval']['display_binary']
  4469. && $meta->type === self::STRING_FIELD)
  4470. || ($_SESSION['tmpval']['display_blob']
  4471. && stristr($meta->type, self::BLOB_FIELD))
  4472. ) {
  4473. // in this case, restart from the original $content
  4474. $result = bin2hex($content);
  4475. list(
  4476. $is_truncated,
  4477. $result,
  4478. // skip 3rd param
  4479. ) = $this->_getPartialText($result);
  4480. }
  4481. /* Create link to download */
  4482. // in PHP < 5.5, empty() only checks variables
  4483. $tmpdb = $this->__get('db');
  4484. if ((count($url_params) > 0) && (! empty($tmpdb) && ! empty($meta->orgtable))) {
  4485. $result = '<a href="tbl_get_field.php'
  4486. . PMA_URL_getCommon($url_params)
  4487. . '" class="disableAjax">'
  4488. . $result . '</a>';
  4489. }
  4490. return($result);
  4491. } // end of the '_handleNonPrintableContents()' function
  4492. /**
  4493. * Retrieves the associated foreign key info for a data cell
  4494. *
  4495. * @param array $map the list of relations
  4496. * @param object $meta the meta-information about the field
  4497. * @param string $where_comparison data for the where clause
  4498. *
  4499. * @return string formatted data
  4500. *
  4501. * @access private
  4502. *
  4503. */
  4504. private function _getFromForeign($map, $meta, $where_comparison)
  4505. {
  4506. $dispsql = 'SELECT '
  4507. . PMA_Util::backquote($map[$meta->name][2])
  4508. . ' FROM '
  4509. . PMA_Util::backquote($map[$meta->name][3])
  4510. . '.'
  4511. . PMA_Util::backquote($map[$meta->name][0])
  4512. . ' WHERE '
  4513. . PMA_Util::backquote($map[$meta->name][1])
  4514. . $where_comparison;
  4515. $dispresult = $GLOBALS['dbi']->tryQuery(
  4516. $dispsql,
  4517. null,
  4518. PMA_DatabaseInterface::QUERY_STORE
  4519. );
  4520. if ($dispresult && $GLOBALS['dbi']->numRows($dispresult) > 0) {
  4521. list($dispval) = $GLOBALS['dbi']->fetchRow($dispresult, 0);
  4522. } else {
  4523. $dispval = __('Link not found!');
  4524. }
  4525. @$GLOBALS['dbi']->freeResult($dispresult);
  4526. return $dispval;
  4527. }
  4528. /**
  4529. * Prepares the displayable content of a data cell in Browse mode,
  4530. * taking into account foreign key description field and transformations
  4531. *
  4532. * @param string $class css classes for the td element
  4533. * @param bool $condition_field whether the column is a part of
  4534. * the where clause
  4535. * @param array $analyzed_sql_results the analyzed query
  4536. * @param object $meta the meta-information about the
  4537. * field
  4538. * @param array $map the list of relations
  4539. * @param string $data data
  4540. * @param object|string $transformation_plugin transformation plugin.
  4541. * Can also be the default function:
  4542. * PMA_mimeDefaultFunction
  4543. * @param string $default_function default function
  4544. * @param string $nowrap 'nowrap' if the content should
  4545. * not be wrapped
  4546. * @param string $where_comparison data for the where clause
  4547. * @param array $transform_options options for transformation
  4548. * @param bool $is_field_truncated whether the field is truncated
  4549. * @param string $original_length of a truncated column, or ''
  4550. *
  4551. * @return string formatted data
  4552. *
  4553. * @access private
  4554. *
  4555. * @see _getDataCellForNumericColumns(), _getDataCellForGeometryColumns(),
  4556. * _getDataCellForNonNumericColumns(),
  4557. *
  4558. */
  4559. private function _getRowData(
  4560. $class, $condition_field, $analyzed_sql_results, $meta, $map, $data,
  4561. $transformation_plugin, $default_function, $nowrap, $where_comparison,
  4562. $transform_options, $is_field_truncated, $original_length=''
  4563. ) {
  4564. $relational_display = $_SESSION['tmpval']['relational_display'];
  4565. $printview = $this->__get('printview');
  4566. $decimals = isset($meta->decimals) ? $meta->decimals : '-1';
  4567. $result = '<td data-decimals="' . $decimals . '"'
  4568. . ' data-type="' . $meta->type . '"';
  4569. if (! empty($original_length)) {
  4570. // cannot use data-original-length
  4571. $result .= ' data-originallength="' . $original_length . '"';
  4572. }
  4573. $result .= ' class="'
  4574. . $this->_addClass(
  4575. $class, $condition_field, $meta, $nowrap,
  4576. $is_field_truncated, $transformation_plugin, $default_function
  4577. )
  4578. . '">';
  4579. if (!empty($analyzed_sql_results['statement']->expr)) {
  4580. foreach ($analyzed_sql_results['statement']->expr as $expr) {
  4581. if ((empty($expr->alias)) || (empty($expr->column))) {
  4582. continue;
  4583. }
  4584. if (strcasecmp($meta->name, $expr->alias) == 0) {
  4585. $meta->name = $expr->column;
  4586. }
  4587. }
  4588. }
  4589. if (isset($map[$meta->name])) {
  4590. // Field to display from the foreign table?
  4591. if (isset($map[$meta->name][2])
  4592. && /*overload*/mb_strlen($map[$meta->name][2])
  4593. ) {
  4594. $dispval = $this->_getFromForeign(
  4595. $map, $meta, $where_comparison
  4596. );
  4597. } else {
  4598. $dispval = '';
  4599. } // end if... else...
  4600. if (isset($printview) && ($printview == '1')) {
  4601. $result .= ($transformation_plugin != $default_function
  4602. ? $transformation_plugin->applyTransformation(
  4603. $data,
  4604. $transform_options,
  4605. $meta
  4606. )
  4607. : $default_function($data)
  4608. )
  4609. . ' <code>[-&gt;' . $dispval . ']</code>';
  4610. } else {
  4611. if ($relational_display == self::RELATIONAL_KEY) {
  4612. // user chose "relational key" in the display options, so
  4613. // the title contains the display field
  4614. $title = (! empty($dispval))
  4615. ? ' title="' . htmlspecialchars($dispval) . '"'
  4616. : '';
  4617. } else {
  4618. $title = ' title="' . htmlspecialchars($data) . '"';
  4619. }
  4620. $_url_params = array(
  4621. 'db' => $map[$meta->name][3],
  4622. 'table' => $map[$meta->name][0],
  4623. 'pos' => '0',
  4624. 'sql_query' => 'SELECT * FROM '
  4625. . PMA_Util::backquote($map[$meta->name][3]) . '.'
  4626. . PMA_Util::backquote($map[$meta->name][0])
  4627. . ' WHERE '
  4628. . PMA_Util::backquote($map[$meta->name][1])
  4629. . $where_comparison,
  4630. );
  4631. $result .= '<a class="ajax" href="sql.php'
  4632. . PMA_URL_getCommon($_url_params)
  4633. . '"' . $title . '>';
  4634. if ($transformation_plugin != $default_function) {
  4635. // always apply a transformation on the real data,
  4636. // not on the display field
  4637. $result .= $transformation_plugin->applyTransformation(
  4638. $data,
  4639. $transform_options,
  4640. $meta
  4641. );
  4642. } else {
  4643. if ($relational_display == self::RELATIONAL_DISPLAY_COLUMN
  4644. && ! empty($map[$meta->name][2])
  4645. ) {
  4646. // user chose "relational display field" in the
  4647. // display options, so show display field in the cell
  4648. $result .= $default_function($dispval);
  4649. } else {
  4650. // otherwise display data in the cell
  4651. $result .= $default_function($data);
  4652. }
  4653. }
  4654. $result .= '</a>';
  4655. }
  4656. } else {
  4657. $result .= ($transformation_plugin != $default_function
  4658. ? $transformation_plugin->applyTransformation(
  4659. $data,
  4660. $transform_options,
  4661. $meta
  4662. )
  4663. : $default_function($data)
  4664. );
  4665. }
  4666. $result .= '</td>' . "\n";
  4667. return $result;
  4668. } // end of the '_getRowData()' function
  4669. /**
  4670. * Prepares a checkbox for multi-row submits
  4671. *
  4672. * @param string $del_url delete url
  4673. * @param array $displayParts array with explicit indexes for all
  4674. * the display elements
  4675. * @param string $row_no the row number
  4676. * @param string $where_clause_html url encoded where clause
  4677. * @param array $condition_array array of conditions in the where clause
  4678. * @param string $id_suffix suffix for the id
  4679. * @param string $class css classes for the td element
  4680. *
  4681. * @return string the generated HTML
  4682. *
  4683. * @access private
  4684. *
  4685. * @see _getTableBody(), _getCheckboxAndLinks()
  4686. */
  4687. private function _getCheckboxForMultiRowSubmissions(
  4688. $del_url, $displayParts, $row_no, $where_clause_html, $condition_array,
  4689. $id_suffix, $class
  4690. ) {
  4691. $ret = '';
  4692. if (! empty($del_url) && $displayParts['del_lnk'] != self::KILL_PROCESS) {
  4693. $ret .= '<td ';
  4694. if (! empty($class)) {
  4695. $ret .= 'class="' . $class . '"';
  4696. }
  4697. $ret .= ' class="center print_ignore">'
  4698. . '<input type="checkbox" id="id_rows_to_delete'
  4699. . $row_no . $id_suffix
  4700. . '" name="rows_to_delete[' . $row_no . ']"'
  4701. . ' class="multi_checkbox checkall"'
  4702. . ' value="' . $where_clause_html . '" '
  4703. . ' />'
  4704. . '<input type="hidden" class="condition_array" value="'
  4705. . htmlspecialchars(json_encode($condition_array)) . '" />'
  4706. . ' </td>';
  4707. }
  4708. return $ret;
  4709. } // end of the '_getCheckboxForMultiRowSubmissions()' function
  4710. /**
  4711. * Prepares an Edit link
  4712. *
  4713. * @param string $edit_url edit url
  4714. * @param string $class css classes for td element
  4715. * @param string $edit_str text for the edit link
  4716. * @param string $where_clause where clause
  4717. * @param string $where_clause_html url encoded where clause
  4718. *
  4719. * @return string the generated HTML
  4720. *
  4721. * @access private
  4722. *
  4723. * @see _getTableBody(), _getCheckboxAndLinks()
  4724. */
  4725. private function _getEditLink(
  4726. $edit_url, $class, $edit_str, $where_clause, $where_clause_html
  4727. ) {
  4728. $ret = '';
  4729. if (! empty($edit_url)) {
  4730. $ret .= '<td class="' . $class . ' center print_ignore" ' . ' ><span class="nowrap">'
  4731. . PMA_Util::linkOrButton(
  4732. $edit_url, $edit_str, array(), false
  4733. );
  4734. /*
  4735. * Where clause for selecting this row uniquely is provided as
  4736. * a hidden input. Used by jQuery scripts for handling grid editing
  4737. */
  4738. if (! empty($where_clause)) {
  4739. $ret .= '<input type="hidden" class="where_clause" value ="'
  4740. . $where_clause_html . '" />';
  4741. }
  4742. $ret .= '</span></td>';
  4743. }
  4744. return $ret;
  4745. } // end of the '_getEditLink()' function
  4746. /**
  4747. * Prepares an Copy link
  4748. *
  4749. * @param string $copy_url copy url
  4750. * @param string $copy_str text for the copy link
  4751. * @param string $where_clause where clause
  4752. * @param string $where_clause_html url encoded where clause
  4753. * @param string $class css classes for the td element
  4754. *
  4755. * @return string the generated HTML
  4756. *
  4757. * @access private
  4758. *
  4759. * @see _getTableBody(), _getCheckboxAndLinks()
  4760. */
  4761. private function _getCopyLink(
  4762. $copy_url, $copy_str, $where_clause, $where_clause_html, $class
  4763. ) {
  4764. $ret = '';
  4765. if (! empty($copy_url)) {
  4766. $ret .= '<td class="';
  4767. if (! empty($class)) {
  4768. $ret .= $class . ' ';
  4769. }
  4770. $ret .= 'center print_ignore" ' . ' ><span class="nowrap">'
  4771. . PMA_Util::linkOrButton(
  4772. $copy_url, $copy_str, array(), false
  4773. );
  4774. /*
  4775. * Where clause for selecting this row uniquely is provided as
  4776. * a hidden input. Used by jQuery scripts for handling grid editing
  4777. */
  4778. if (! empty($where_clause)) {
  4779. $ret .= '<input type="hidden" class="where_clause" value="'
  4780. . $where_clause_html . '" />';
  4781. }
  4782. $ret .= '</span></td>';
  4783. }
  4784. return $ret;
  4785. } // end of the '_getCopyLink()' function
  4786. /**
  4787. * Prepares a Delete link
  4788. *
  4789. * @param string $del_url delete url
  4790. * @param string $del_str text for the delete link
  4791. * @param string $js_conf text for the JS confirmation
  4792. * @param string $class css classes for the td element
  4793. *
  4794. * @return string the generated HTML
  4795. *
  4796. * @access private
  4797. *
  4798. * @see _getTableBody(), _getCheckboxAndLinks()
  4799. */
  4800. private function _getDeleteLink($del_url, $del_str, $js_conf, $class)
  4801. {
  4802. $ret = '';
  4803. if (! empty($del_url)) {
  4804. $ret .= '<td class="';
  4805. if (! empty($class)) {
  4806. $ret .= $class . ' ';
  4807. }
  4808. $ajax = PMA_Response::getInstance()->isAjax() ? ' ajax' : '';
  4809. $ret .= 'center print_ignore" ' . ' >'
  4810. . PMA_Util::linkOrButton(
  4811. $del_url, $del_str, array('class' => 'delete_row requireConfirm' . $ajax), false
  4812. )
  4813. . '<div class="hide">' . $js_conf . '</div>'
  4814. . '</td>';
  4815. }
  4816. return $ret;
  4817. } // end of the '_getDeleteLink()' function
  4818. /**
  4819. * Prepare checkbox and links at some position (left or right)
  4820. * (only called for horizontal mode)
  4821. *
  4822. * @param string $position the position of the checkbox and links
  4823. * @param string $del_url delete url
  4824. * @param array $displayParts array with explicit indexes for all the
  4825. * display elements
  4826. * @param string $row_no row number
  4827. * @param string $where_clause where clause
  4828. * @param string $where_clause_html url encoded where clause
  4829. * @param array $condition_array array of conditions in the where clause
  4830. * @param string $edit_url edit url
  4831. * @param string $copy_url copy url
  4832. * @param string $class css classes for the td elements
  4833. * @param string $edit_str text for the edit link
  4834. * @param string $copy_str text for the copy link
  4835. * @param string $del_str text for the delete link
  4836. * @param string $js_conf text for the JS confirmation
  4837. *
  4838. * @return string the generated HTML
  4839. *
  4840. * @access private
  4841. *
  4842. * @see _getPlacedLinks()
  4843. */
  4844. private function _getCheckboxAndLinks(
  4845. $position, $del_url, $displayParts, $row_no, $where_clause,
  4846. $where_clause_html, $condition_array,
  4847. $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf
  4848. ) {
  4849. $ret = '';
  4850. if ($position == self::POSITION_LEFT) {
  4851. $ret .= $this->_getCheckboxForMultiRowSubmissions(
  4852. $del_url, $displayParts, $row_no, $where_clause_html,
  4853. $condition_array, '_left', ''
  4854. );
  4855. $ret .= $this->_getEditLink(
  4856. $edit_url, $class, $edit_str, $where_clause, $where_clause_html
  4857. );
  4858. $ret .= $this->_getCopyLink(
  4859. $copy_url, $copy_str, $where_clause, $where_clause_html, ''
  4860. );
  4861. $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
  4862. } elseif ($position == self::POSITION_RIGHT) {
  4863. $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
  4864. $ret .= $this->_getCopyLink(
  4865. $copy_url, $copy_str, $where_clause, $where_clause_html, ''
  4866. );
  4867. $ret .= $this->_getEditLink(
  4868. $edit_url, $class, $edit_str, $where_clause, $where_clause_html
  4869. );
  4870. $ret .= $this->_getCheckboxForMultiRowSubmissions(
  4871. $del_url, $displayParts, $row_no, $where_clause_html,
  4872. $condition_array, '_right', ''
  4873. );
  4874. } else { // $position == self::POSITION_NONE
  4875. $ret .= $this->_getCheckboxForMultiRowSubmissions(
  4876. $del_url, $displayParts, $row_no, $where_clause_html,
  4877. $condition_array, '_left', ''
  4878. );
  4879. }
  4880. return $ret;
  4881. } // end of the '_getCheckboxAndLinks()' function
  4882. /**
  4883. * Truncates given string based on LimitChars configuration
  4884. * and Session pftext variable
  4885. * (string is truncated only if necessary)
  4886. *
  4887. * @param string $str string to be truncated
  4888. *
  4889. * @return mixed
  4890. *
  4891. * @access private
  4892. *
  4893. * @see _handleNonPrintableContents(), _getDataCellForGeometryColumns(),
  4894. * _getDataCellForNonNumericColumns
  4895. */
  4896. private function _getPartialText($str)
  4897. {
  4898. $original_length = /*overload*/mb_strlen($str);
  4899. if ($original_length > $GLOBALS['cfg']['LimitChars']
  4900. && $_SESSION['tmpval']['pftext'] === self::DISPLAY_PARTIAL_TEXT
  4901. ) {
  4902. $str = /*overload*/mb_substr(
  4903. $str, 0, $GLOBALS['cfg']['LimitChars']
  4904. ) . '...';
  4905. $truncated = true;
  4906. } else {
  4907. $truncated = false;
  4908. }
  4909. return array($truncated, $str, $original_length);
  4910. }
  4911. }