2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * Hold the PMA\libraries\DisplayResults class
8 namespace PMA\libraries
;
10 use PhpMyAdmin\SqlParser\Utils\Query
;
11 use PMA\libraries\plugins\transformations\Text_Plain_Link
;
12 use PMA\libraries\URL
;
13 use PMA\libraries\Sanitize
;
15 require_once './libraries/transformations.lib.php';
18 * Handle all the functionalities related to displaying results
19 * of sql queries, stored procedure, browsing sql processes or
20 * displaying binary log.
28 const NO_EDIT_OR_DELETE
= 'nn';
29 const UPDATE_ROW
= 'ur';
30 const DELETE_ROW
= 'dr';
31 const KILL_PROCESS
= 'kp';
33 const POSITION_LEFT
= 'left';
34 const POSITION_RIGHT
= 'right';
35 const POSITION_BOTH
= 'both';
36 const POSITION_NONE
= 'none';
38 const PLACE_TOP_DIRECTION_DROPDOWN
= 'top_direction_dropdown';
39 const PLACE_BOTTOM_DIRECTION_DROPDOWN
= 'bottom_direction_dropdown';
41 const DISPLAY_FULL_TEXT
= 'F';
42 const DISPLAY_PARTIAL_TEXT
= 'P';
44 const HEADER_FLIP_TYPE_AUTO
= 'auto';
45 const HEADER_FLIP_TYPE_CSS
= 'css';
46 const HEADER_FLIP_TYPE_FAKE
= 'fake';
48 const DATE_FIELD
= 'date';
49 const DATETIME_FIELD
= 'datetime';
50 const TIMESTAMP_FIELD
= 'timestamp';
51 const TIME_FIELD
= 'time';
52 const STRING_FIELD
= 'string';
53 const GEOMETRY_FIELD
= 'geometry';
54 const BLOB_FIELD
= 'BLOB';
55 const BINARY_FIELD
= 'BINARY';
57 const RELATIONAL_KEY
= 'K';
58 const RELATIONAL_DISPLAY_COLUMN
= 'D';
60 const GEOMETRY_DISP_GEOM
= 'GEOM';
61 const GEOMETRY_DISP_WKT
= 'WKT';
62 const GEOMETRY_DISP_WKB
= 'WKB';
64 const SMART_SORT_ORDER
= 'SMART';
65 const ASCENDING_SORT_DIR
= 'ASC';
66 const DESCENDING_SORT_DIR
= 'DESC';
68 const TABLE_TYPE_INNO_DB
= 'InnoDB';
69 const ALL_ROWS
= 'all';
70 const QUERY_TYPE_SELECT
= 'SELECT';
72 const ROUTINE_PROCEDURE
= 'procedure';
73 const ROUTINE_FUNCTION
= 'function';
75 const ACTION_LINK_CONTENT_ICONS
= 'icons';
76 const ACTION_LINK_CONTENT_TEXT
= 'text';
79 // Declare global fields
81 /** array with properties of the class */
82 private $_property_array = array(
84 /** string Database name */
87 /** string Table name */
90 /** string the URL to go back in case of errors */
93 /** string the SQL query */
97 * integer the total number of rows returned by the SQL query without any
98 * appended "LIMIT" clause programmatically
100 'unlim_num_rows' => null,
102 /** array meta information about fields */
103 'fields_meta' => null,
115 'is_analyse' => null,
117 /** integer the total number of rows returned by the SQL query */
120 /** integer the total number of fields returned by the SQL query */
121 'fields_cnt' => null,
123 /** double time taken for execute the SQL query */
126 /** string path for theme images directory */
127 'pma_theme_image' => null,
136 'is_explain' => null,
142 'is_browse_distinct' => null,
144 /** array table definitions */
150 /** string URL query */
153 /** array column names to highlight */
154 'highlight_columns' => null,
156 /** array holding various display information */
157 'display_params' => null,
159 /** array mime types information of fields */
165 /** random unique ID to distinguish result set */
168 /** where clauses for each row, each table in the row */
169 'whereClauseMap' => array(),
173 * This variable contains the column transformation information
174 * for some of the system databases.
175 * One element of this array represent all relevant columns in all tables in
176 * one specific database
178 public $transformation_info;
182 * Get any property of this class
184 * @param string $property name of the property
186 * @return mixed|void if property exist, value of the relevant property
188 public function __get($property)
190 if (array_key_exists($property, $this->_property_array
)) {
191 return $this->_property_array
[$property];
197 * Set values for any property of this class
199 * @param string $property name of the property
200 * @param mixed $value value to set
204 public function __set($property, $value)
206 if (array_key_exists($property, $this->_property_array
)) {
207 $this->_property_array
[$property] = $value;
213 * Constructor for DisplayResults class
215 * @param string $db the database name
216 * @param string $table the table name
217 * @param string $goto the URL to go back in case of errors
218 * @param string $sql_query the SQL query
222 public function __construct($db, $table, $goto, $sql_query)
224 $this->_setDefaultTransformations();
226 $this->__set('db', $db);
227 $this->__set('table', $table);
228 $this->__set('goto', $goto);
229 $this->__set('sql_query', $sql_query);
230 $this->__set('unique_id', rand());
234 * Sets default transformations for some columns
238 private function _setDefaultTransformations()
240 $json_highlighting_data = array(
241 'libraries/plugins/transformations/output/Text_Plain_Json.php',
242 'PMA\libraries\plugins\transformations\output\Text_Plain_Json',
245 $sql_highlighting_data = array(
246 'libraries/plugins/transformations/output/Text_Plain_Sql.php',
247 'PMA\libraries\plugins\transformations\output\Text_Plain_Sql',
250 $blob_sql_highlighting_data = array(
251 'libraries/plugins/transformations/output/Text_Octetstream_Sql.php',
252 'PMA\libraries\plugins\transformations\output\Text_Octetstream_Sql',
256 'libraries/plugins/transformations/Text_Plain_Link.php',
257 'PMA\libraries\plugins\transformations\Text_Plain_Link',
260 $this->transformation_info
= array(
261 'information_schema' => array(
263 'event_definition' => $sql_highlighting_data
265 'processlist' => array(
266 'info' => $sql_highlighting_data
269 'routine_definition' => $sql_highlighting_data
272 'action_statement' => $sql_highlighting_data
275 'view_definition' => $sql_highlighting_data
280 'body' => $blob_sql_highlighting_data,
281 'body_utf8' => $blob_sql_highlighting_data
283 'general_log' => array(
284 'argument' => $sql_highlighting_data
286 'help_category' => array(
289 'help_topic' => array(
290 'example' => $sql_highlighting_data,
294 'param_list' => $blob_sql_highlighting_data,
295 'returns' => $blob_sql_highlighting_data,
296 'body' => $blob_sql_highlighting_data,
297 'body_utf8' => $blob_sql_highlighting_data
300 'sql_text' => $sql_highlighting_data
305 $cfgRelation = PMA_getRelationsParam();
306 if ($cfgRelation['db']) {
307 $this->transformation_info
[$cfgRelation['db']] = array();
308 $relDb = &$this->transformation_info
[$cfgRelation['db']];
309 if (! empty($cfgRelation['history'])) {
310 $relDb[$cfgRelation['history']] = array(
311 'sqlquery' => $sql_highlighting_data
314 if (! empty($cfgRelation['bookmark'])) {
315 $relDb[$cfgRelation['bookmark']] = array(
316 'query' => $sql_highlighting_data
319 if (! empty($cfgRelation['tracking'])) {
320 $relDb[$cfgRelation['tracking']] = array(
321 'schema_sql' => $sql_highlighting_data,
322 'data_sql' => $sql_highlighting_data
325 if (! empty($cfgRelation['favorite'])) {
326 $relDb[$cfgRelation['favorite']] = array(
327 'tables' => $json_highlighting_data
330 if (! empty($cfgRelation['recent'])) {
331 $relDb[$cfgRelation['recent']] = array(
332 'tables' => $json_highlighting_data
335 if (! empty($cfgRelation['savedsearches'])) {
336 $relDb[$cfgRelation['savedsearches']] = array(
337 'search_data' => $json_highlighting_data
340 if (! empty($cfgRelation['designer_settings'])) {
341 $relDb[$cfgRelation['designer_settings']] = array(
342 'settings_data' => $json_highlighting_data
345 if (! empty($cfgRelation['table_uiprefs'])) {
346 $relDb[$cfgRelation['table_uiprefs']] = array(
347 'prefs' => $json_highlighting_data
350 if (! empty($cfgRelation['userconfig'])) {
351 $relDb[$cfgRelation['userconfig']] = array(
352 'config_data' => $json_highlighting_data
355 if (! empty($cfgRelation['export_templates'])) {
356 $relDb[$cfgRelation['export_templates']] = array(
357 'template_data' => $json_highlighting_data
364 * Set properties which were not initialized at the constructor
366 * @param integer $unlim_num_rows the total number of rows returned by
367 * the SQL query without any appended
368 * "LIMIT" clause programmatically
369 * @param array $fields_meta meta information about fields
370 * @param boolean $is_count statement is SELECT COUNT
371 * @param integer $is_export statement contains INTO OUTFILE
372 * @param boolean $is_func statement contains a function like SUM()
373 * @param integer $is_analyse statement contains PROCEDURE ANALYSE
374 * @param integer $num_rows total no. of rows returned by SQL query
375 * @param integer $fields_cnt total no.of fields returned by SQL query
376 * @param double $querytime time taken for execute the SQL query
377 * @param string $pmaThemeImage path for theme images directory
378 * @param string $text_dir text direction
379 * @param boolean $is_maint statement contains a maintenance command
380 * @param boolean $is_explain statement contains EXPLAIN
381 * @param boolean $is_show statement contains SHOW
382 * @param array $showtable table definitions
383 * @param string $printview print view was requested
384 * @param string $url_query URL query
385 * @param boolean $editable whether the results set is editable
386 * @param boolean $is_browse_dist whether browsing distinct values
392 public function setProperties(
393 $unlim_num_rows, $fields_meta, $is_count, $is_export, $is_func,
394 $is_analyse, $num_rows, $fields_cnt, $querytime, $pmaThemeImage, $text_dir,
395 $is_maint, $is_explain, $is_show, $showtable, $printview, $url_query,
396 $editable, $is_browse_dist
399 $this->__set('unlim_num_rows', $unlim_num_rows);
400 $this->__set('fields_meta', $fields_meta);
401 $this->__set('is_count', $is_count);
402 $this->__set('is_export', $is_export);
403 $this->__set('is_func', $is_func);
404 $this->__set('is_analyse', $is_analyse);
405 $this->__set('num_rows', $num_rows);
406 $this->__set('fields_cnt', $fields_cnt);
407 $this->__set('querytime', $querytime);
408 $this->__set('pma_theme_image', $pmaThemeImage);
409 $this->__set('text_dir', $text_dir);
410 $this->__set('is_maint', $is_maint);
411 $this->__set('is_explain', $is_explain);
412 $this->__set('is_show', $is_show);
413 $this->__set('showtable', $showtable);
414 $this->__set('printview', $printview);
415 $this->__set('url_query', $url_query);
416 $this->__set('editable', $editable);
417 $this->__set('is_browse_distinct', $is_browse_dist);
419 } // end of the 'setProperties()' function
423 * Defines the parts to display for a print view
425 * @param array $displayParts the parts to display
427 * @return array $displayParts the modified display parts
432 private function _setDisplayPartsForPrintView($displayParts)
434 // set all elements to false!
435 $displayParts['edit_lnk'] = self
::NO_EDIT_OR_DELETE
; // no edit link
436 $displayParts['del_lnk'] = self
::NO_EDIT_OR_DELETE
; // no delete link
437 $displayParts['sort_lnk'] = (string) '0';
438 $displayParts['nav_bar'] = (string) '0';
439 $displayParts['bkm_form'] = (string) '0';
440 $displayParts['text_btn'] = (string) '0';
441 $displayParts['pview_lnk'] = (string) '0';
443 return $displayParts;
447 * Defines the parts to display for a SHOW statement
449 * @param array $displayParts the parts to display
451 * @return array $displayParts the modified display parts
456 private function _setDisplayPartsForShow($displayParts)
459 '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
460 . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
462 $this->__get('sql_query'), $which
465 $bIsProcessList = isset($which[1]);
466 if ($bIsProcessList) {
467 $str = ' ' . strtoupper($which[1]);
468 $bIsProcessList = $bIsProcessList
469 && strpos($str, 'PROCESSLIST') > 0;
472 if ($bIsProcessList) {
474 $displayParts['edit_lnk'] = self
::NO_EDIT_OR_DELETE
;
475 // "kill process" type edit link
476 $displayParts['del_lnk'] = self
::KILL_PROCESS
;
478 // Default case -> no links
480 $displayParts['edit_lnk'] = self
::NO_EDIT_OR_DELETE
;
482 $displayParts['del_lnk'] = self
::NO_EDIT_OR_DELETE
;
485 $displayParts['sort_lnk'] = (string) '0';
486 $displayParts['nav_bar'] = (string) '0';
487 $displayParts['bkm_form'] = (string) '1';
488 $displayParts['text_btn'] = (string) '1';
489 $displayParts['pview_lnk'] = (string) '1';
491 return $displayParts;
495 * Defines the parts to display for statements not related to data
497 * @param array $displayParts the parts to display
499 * @return array $displayParts the modified display parts
504 private function _setDisplayPartsForNonData($displayParts)
506 // Statement is a "SELECT COUNT", a
507 // "CHECK/ANALYZE/REPAIR/OPTIMIZE/CHECKSUM", an "EXPLAIN" one or
508 // contains a "PROC ANALYSE" part
509 $displayParts['edit_lnk'] = self
::NO_EDIT_OR_DELETE
; // no edit link
510 $displayParts['del_lnk'] = self
::NO_EDIT_OR_DELETE
; // no delete link
511 $displayParts['sort_lnk'] = (string) '0';
512 $displayParts['nav_bar'] = (string) '0';
513 $displayParts['bkm_form'] = (string) '1';
515 if ($this->__get('is_maint')) {
516 $displayParts['text_btn'] = (string) '1';
518 $displayParts['text_btn'] = (string) '0';
520 $displayParts['pview_lnk'] = (string) '1';
522 return $displayParts;
526 * Defines the parts to display for other statements (probably SELECT)
528 * @param array $displayParts the parts to display
530 * @return array $displayParts the modified display parts
535 private function _setDisplayPartsForSelect($displayParts)
537 // Other statements (ie "SELECT" ones) -> updates
538 // $displayParts['edit_lnk'], $displayParts['del_lnk'] and
539 // $displayParts['text_btn'] (keeps other default values)
541 $fields_meta = $this->__get('fields_meta');
543 $displayParts['text_btn'] = (string) '1';
544 $number_of_columns = $this->__get('fields_cnt');
546 for ($i = 0; $i < $number_of_columns; $i++
) {
548 $is_link = ($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
549 ||
($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)
550 ||
($displayParts['sort_lnk'] != '0');
552 // Displays edit/delete/sort/insert links?
555 && $fields_meta[$i]->table
!= ''
556 && $fields_meta[$i]->table
!= $prev_table
558 // don't display links
559 $displayParts['edit_lnk'] = self
::NO_EDIT_OR_DELETE
;
560 $displayParts['del_lnk'] = self
::NO_EDIT_OR_DELETE
;
562 * @todo May be problematic with same field names
563 * in two joined table.
565 // $displayParts['sort_lnk'] = (string) '0';
566 if ($displayParts['text_btn'] == '1') {
571 // Always display print view link
572 $displayParts['pview_lnk'] = (string) '1';
573 if ($fields_meta[$i]->table
!= '') {
574 $prev_table = $fields_meta[$i]->table
;
578 if ($prev_table == '') { // no table for any of the columns
579 // don't display links
580 $displayParts['edit_lnk'] = self
::NO_EDIT_OR_DELETE
;
581 $displayParts['del_lnk'] = self
::NO_EDIT_OR_DELETE
;
584 return $displayParts;
588 * Defines the parts to display for the results of a SQL query
589 * and the total number of rows
591 * @param array $displayParts the parts to display (see a few
592 * lines above for explanations)
594 * @return array the first element is an array with explicit indexes
595 * for all the display elements
596 * the second element is the total number of rows returned
597 * by the SQL query without any programmatically appended
598 * LIMIT clause (just a copy of $unlim_num_rows if it exists,
599 * else computed inside this function)
606 private function _setDisplayPartsAndTotal($displayParts)
610 // 1. Following variables are needed for use in isset/empty or
611 // use with array indexes or safe use in foreach
612 $db = $this->__get('db');
613 $table = $this->__get('table');
614 $unlim_num_rows = $this->__get('unlim_num_rows');
615 $num_rows = $this->__get('num_rows');
616 $printview = $this->__get('printview');
618 // 2. Updates the display parts
619 if ($printview == '1') {
620 $displayParts = $this->_setDisplayPartsForPrintView($displayParts);
622 } elseif ($this->__get('is_count') ||
$this->__get('is_analyse')
623 ||
$this->__get('is_maint') ||
$this->__get('is_explain')
625 $displayParts = $this->_setDisplayPartsForNonData($displayParts);
627 } elseif ($this->__get('is_show')) {
628 $displayParts = $this->_setDisplayPartsForShow($displayParts);
631 $displayParts = $this->_setDisplayPartsForSelect($displayParts);
632 } // end if..elseif...else
634 // 3. Gets the total number of rows if it is unknown
635 if (isset($unlim_num_rows) && $unlim_num_rows != '') {
636 $the_total = $unlim_num_rows;
637 } elseif ((($displayParts['nav_bar'] == '1')
638 ||
($displayParts['sort_lnk'] == '1'))
639 && (strlen($db) > 0 && strlen($table) > 0)
641 $the_total = $GLOBALS['dbi']->getTable($db, $table)->countRecords();
644 // if for COUNT query, number of rows returned more than 1
645 // (may be being used GROUP BY)
646 if ($this->__get('is_count') && isset($num_rows) && $num_rows > 1) {
647 $displayParts['nav_bar'] = (string) '1';
648 $displayParts['sort_lnk'] = (string) '1';
650 // 4. If navigation bar or sorting fields names URLs should be
651 // displayed but there is only one row, change these settings to
653 if ($displayParts['nav_bar'] == '1' ||
$displayParts['sort_lnk'] == '1') {
655 // - Do not display sort links if less than 2 rows.
656 // - For a VIEW we (probably) did not count the number of rows
657 // so don't test this number here, it would remove the possibility
658 // of sorting VIEW results.
659 $_table = new Table($table, $db);
660 if (isset($unlim_num_rows)
661 && ($unlim_num_rows < 2)
662 && ! $_table->isView()
664 $displayParts['sort_lnk'] = (string) '0';
668 return array($displayParts, $the_total);
670 } // end of the 'setDisplayPartsAndTotal()' function
674 * Return true if we are executing a query in the form of
675 * "SELECT * FROM <a table> ..."
677 * @param array $analyzed_sql_results analyzed sql results
683 * @see _getTableHeaders(), _getColumnParams()
685 private function _isSelect($analyzed_sql_results)
687 return ! ($this->__get('is_count')
688 ||
$this->__get('is_export')
689 ||
$this->__get('is_func')
690 ||
$this->__get('is_analyse'))
691 && !empty($analyzed_sql_results['select_from'])
692 && !empty($analyzed_sql_results['statement']->from
)
693 && (count($analyzed_sql_results['statement']->from
) == 1)
694 && !empty($analyzed_sql_results['statement']->from
[0]->table
);
699 * Get a navigation button
701 * @param string $caption iconic caption for button
702 * @param string $title text for button
703 * @param integer $pos position for next query
704 * @param string $html_sql_query query ready for display
705 * @param boolean $back whether 'begin' or 'previous'
706 * @param string $onsubmit optional onsubmit clause
707 * @param string $input_for_real_end optional hidden field for special treatment
708 * @param string $onclick optional onclick clause
710 * @return string html content
714 * @see _getMoveBackwardButtonsForTableNavigation(),
715 * _getMoveForwardButtonsForTableNavigation()
717 private function _getTableNavigationButton(
718 $caption, $title, $pos, $html_sql_query, $back, $onsubmit = '',
719 $input_for_real_end = '', $onclick = ''
722 $caption_output = '';
724 if (Util
::showIcons('TableNavigationLinksMode')) {
725 $caption_output .= $caption;
727 if (Util
::showText('TableNavigationLinksMode')) {
728 $caption_output .= ' ' . $title;
731 if (Util
::showText('TableNavigationLinksMode')) {
732 $caption_output .= $title;
734 if (Util
::showIcons('TableNavigationLinksMode')) {
735 $caption_output .= ' ' . $caption;
738 $title_output = ' title="' . $title . '"';
741 . '<form action="sql.php" method="post" ' . $onsubmit . '>'
742 . URL
::getHiddenInputs(
743 $this->__get('db'), $this->__get('table')
745 . '<input type="hidden" name="sql_query" value="'
746 . $html_sql_query . '" />'
747 . '<input type="hidden" name="pos" value="' . $pos . '" />'
748 . '<input type="hidden" name="is_browse_distinct" value="'
749 . $this->__get('is_browse_distinct') . '" />'
750 . '<input type="hidden" name="goto" value="' . $this->__get('goto')
752 . $input_for_real_end
753 . '<input type="submit" name="navig"'
755 . 'value="' . $caption_output . '" ' . $title_output . $onclick . ' />'
759 } // end function _getTableNavigationButton()
763 * Possibly return a page selector for table navigation
765 * @param string $table_navigation_html the current navigation HTML
767 * @return array ($table_navigation_html, $nbTotalPage)
772 private function _getHtmlPageSelector($table_navigation_html)
775 $_SESSION['tmpval']['pos']
776 / $_SESSION['tmpval']['max_rows']
779 $nbTotalPage = @ceil
(
780 $this->__get('unlim_num_rows')
781 / $_SESSION['tmpval']['max_rows']
784 if ($nbTotalPage > 1) {
785 $table_navigation_html .= '<td>';
786 $_url_params = array(
787 'db' => $this->__get('db'),
788 'table' => $this->__get('table'),
789 'sql_query' => $this->__get('sql_query'),
790 'goto' => $this->__get('goto'),
791 'is_browse_distinct' => $this->__get('is_browse_distinct'),
794 //<form> to keep the form alignment of button < and <<
795 // and also to know what to execute when the selector changes
796 $table_navigation_html .= '<form action="sql.php'
797 . URL
::getCommon($_url_params)
798 . '" method="post">';
800 $table_navigation_html .= Util
::pageselector(
802 $_SESSION['tmpval']['max_rows'],
803 $pageNow, $nbTotalPage, 200, 5, 5, 20, 10
806 $table_navigation_html .= '</form>'
809 return array($table_navigation_html, $nbTotalPage);
813 * Get a navigation bar to browse among the results of a SQL query
815 * @param integer $pos_next the offset for the "next" page
816 * @param integer $pos_prev the offset for the "previous" page
817 * @param boolean $is_innodb whether its InnoDB or not
818 * @param string $sort_by_key_html the sort by key dialog
820 * @return string html content
826 private function _getTableNavigation(
827 $pos_next, $pos_prev, $is_innodb, $sort_by_key_html
830 $table_navigation_html = '';
832 // here, using htmlentities() would cause problems if the query
833 // contains accented characters
834 $html_sql_query = htmlspecialchars($this->__get('sql_query'));
837 $table_navigation_html
838 .= '<table class="navigation nospacing nopadding print_ignore">'
840 . '<td class="navigation_separator"></td>';
842 // Move to the beginning or to the previous page
843 if ($_SESSION['tmpval']['pos']
844 && ($_SESSION['tmpval']['max_rows'] != self
::ALL_ROWS
)
847 $table_navigation_html
848 .= $this->_getMoveBackwardButtonsForTableNavigation(
849 $html_sql_query, $pos_prev
856 // (unless we are showing all records)
857 if ($_SESSION['tmpval']['max_rows'] != self
::ALL_ROWS
) {
859 $table_navigation_html,
861 ) = $this->_getHtmlPageSelector($table_navigation_html);
864 $showing_all = false;
865 if ($_SESSION['tmpval']['max_rows'] == self
::ALL_ROWS
) {
869 // Move to the next page or to the last one
870 $endpos = $_SESSION['tmpval']['pos']
871 +
$_SESSION['tmpval']['max_rows'];
873 if ($this->__get('unlim_num_rows') === false // view with unknown number of rows
874 ||
($endpos < $this->__get('unlim_num_rows')
875 && $this->__get('num_rows') >= $_SESSION['tmpval']['max_rows']
876 && $_SESSION['tmpval']['max_rows'] != self
::ALL_ROWS
)
879 $table_navigation_html
880 .= $this->_getMoveForwardButtonsForTableNavigation(
881 $html_sql_query, $pos_next, $is_innodb
886 // show separator if pagination happen
887 if ($nbTotalPage > 1) {
888 $table_navigation_html
889 .= '<td><div class="navigation_separator">|</div></td>';
892 // Display the "Show all" button if allowed
893 if ($GLOBALS['cfg']['ShowAll'] ||
($this->__get('unlim_num_rows') <= 500) ) {
895 $table_navigation_html .= $this->_getShowAllCheckboxForTableNavigation(
896 $showing_all, $html_sql_query
899 $table_navigation_html
900 .= '<td><div class="navigation_separator">|</div></td>';
904 $table_navigation_html .= '<td>'
905 . '<div class="save_edited hide">'
906 . '<input type="submit" value="' . __('Save edited data') . '" />'
907 . '<div class="navigation_separator">|</div>'
911 . '<div class="restore_column hide">'
912 . '<input type="submit" value="' . __('Restore column order') . '" />'
913 . '<div class="navigation_separator">|</div>'
917 // if displaying a VIEW, $unlim_num_rows could be zero because
918 // of $cfg['MaxExactCountViews']; in this case, avoid passing
919 // the 5th parameter to checkFormElementInRange()
920 // (this means we can't validate the upper limit
921 $table_navigation_html .= '<td class="navigation_goto">';
923 $table_navigation_html .= '<form action="sql.php" method="post" '
924 . 'onsubmit="return '
925 . '(checkFormElementInRange('
927 . '\'session_max_rows\', '
929 . str_replace('\'', '\\\'', __('%d is not valid row number.'))
933 . 'checkFormElementInRange('
937 . str_replace('\'', '\\\'', __('%d is not valid row number.'))
940 . (($this->__get('unlim_num_rows') > 0)
941 ?
', ' . ($this->__get('unlim_num_rows') - 1)
948 $table_navigation_html .= URL
::getHiddenInputs(
949 $this->__get('db'), $this->__get('table')
952 $table_navigation_html .= $this->_getAdditionalFieldsForTableNavigation(
956 $table_navigation_html .= '</form>'
958 . '<td class="navigation_separator"></td>'
960 . '<span>' . __('Filter rows') . ':</span>'
961 . '<input type="text" class="filter_rows"'
962 . ' placeholder="' . __('Search this table') . '"'
963 . ' data-for="' . $this->__get('unique_id') . '" />'
966 $table_navigation_html .= '<td>' . $sort_by_key_html . '</td>';
968 $table_navigation_html .= '<td class="navigation_separator"></td>'
972 return $table_navigation_html;
974 } // end of the '_getTableNavigation()' function
978 * Prepare move backward buttons - previous and first
980 * @param string $html_sql_query the sql encoded by html special characters
981 * @param integer $pos_prev the offset for the "previous" page
983 * @return string html content
987 * @see _getTableNavigation()
989 private function _getMoveBackwardButtonsForTableNavigation(
990 $html_sql_query, $pos_prev
992 return $this->_getTableNavigationButton(
993 '<<', _pgettext('First page', 'Begin'), 0, $html_sql_query, true
995 . $this->_getTableNavigationButton(
996 '<', _pgettext('Previous page', 'Previous'), $pos_prev,
997 $html_sql_query, true
999 } // end of the '_getMoveBackwardButtonsForTableNavigation()' function
1003 * Prepare Show All checkbox for table navigation
1005 * @param bool $showing_all whether all rows are shown currently
1006 * @param string $html_sql_query the sql encoded by html special characters
1008 * @return string html content
1012 * @see _getTableNavigation()
1014 private function _getShowAllCheckboxForTableNavigation(
1015 $showing_all, $html_sql_query
1019 . '<form action="sql.php" method="post">'
1020 . URL
::getHiddenInputs(
1021 $this->__get('db'), $this->__get('table')
1023 . '<input type="hidden" name="sql_query" value="'
1024 . $html_sql_query . '" />'
1025 . '<input type="hidden" name="pos" value="0" />'
1026 . '<input type="hidden" name="is_browse_distinct" value="'
1027 . $this->__get('is_browse_distinct') . '" />'
1028 . '<input type="hidden" name="session_max_rows" value="'
1029 . (! $showing_all ?
'all' : intval($GLOBALS['cfg']['MaxRows'])) . '" />'
1030 . '<input type="hidden" name="goto" value="' . $this->__get('goto')
1032 . '<input type="checkbox" name="navig"'
1033 . ' id="showAll_' . $this->__get('unique_id') . '" class="showAllRows"'
1034 . (! $showing_all ?
'' : ' checked="checked"') . ' value="all" />'
1035 . '<label for="showAll_' . $this->__get('unique_id') . '">'
1036 . __('Show all') . '</label>'
1039 } // end of the '_getShowAllButtonForTableNavigation()' function
1043 * Prepare move forward buttons - next and last
1045 * @param string $html_sql_query the sql encoded by htmlspecialchars()
1046 * @param integer $pos_next the offset for the "next" page
1047 * @param boolean $is_innodb whether it's InnoDB or not
1049 * @return string $buttons_html html content
1053 * @see _getTableNavigation()
1055 private function _getMoveForwardButtonsForTableNavigation(
1056 $html_sql_query, $pos_next, $is_innodb
1059 // display the Next button
1060 $buttons_html = $this->_getTableNavigationButton(
1062 _pgettext('Next page', 'Next'),
1068 // prepare some options for the End button
1070 && $this->__get('unlim_num_rows') > $GLOBALS['cfg']['MaxExactCount']
1072 $input_for_real_end = '<input id="real_end_input" type="hidden" '
1073 . 'name="find_real_end" value="1" />';
1074 // no backquote around this message
1077 $input_for_real_end = $onclick = '';
1080 $maxRows = $_SESSION['tmpval']['max_rows'];
1081 $onsubmit = 'onsubmit="return '
1082 . (($_SESSION['tmpval']['pos']
1084 < $this->__get('unlim_num_rows')
1085 && $this->__get('num_rows') >= $maxRows)
1089 // display the End button
1090 $buttons_html .= $this->_getTableNavigationButton(
1092 _pgettext('Last page', 'End'),
1094 $this->__get('unlim_num_rows')
1095 / $_SESSION['tmpval']['max_rows']
1097 $html_sql_query, false, $onsubmit, $input_for_real_end, $onclick
1100 return $buttons_html;
1102 } // end of the '_getMoveForwardButtonsForTableNavigation()' function
1106 * Prepare fields for table navigation
1109 * @param string $html_sql_query the sql encoded by htmlspecialchars()
1111 * @return string $additional_fields_html html content
1115 * @see _getTableNavigation()
1117 private function _getAdditionalFieldsForTableNavigation(
1121 $additional_fields_html = '';
1123 $additional_fields_html .= '<input type="hidden" name="sql_query" '
1124 . 'value="' . $html_sql_query . '" />'
1125 . '<input type="hidden" name="goto" value="' . $this->__get('goto')
1127 . '<input type="hidden" name="pos" size="3" value="'
1128 // Do not change the position when changing the number of rows
1129 . $_SESSION['tmpval']['pos'] . '" />'
1130 . '<input type="hidden" name="is_browse_distinct" value="'
1131 . $this->__get('is_browse_distinct') . '" />' ;
1133 $numberOfRowsPlaceholder = null;
1134 if ($_SESSION['tmpval']['max_rows'] == self
::ALL_ROWS
) {
1135 $numberOfRowsPlaceholder = __('All');
1138 $numberOfRowsChoices = array(
1145 $additional_fields_html .= __('Number of rows:') . ' ';
1146 $additional_fields_html .= Util
::getDropdown(
1147 'session_max_rows', $numberOfRowsChoices,
1148 $_SESSION['tmpval']['max_rows'], '',
1149 'autosubmit', $numberOfRowsPlaceholder
1152 return $additional_fields_html;
1154 } // end of the '_getAdditionalFieldsForTableNavigation()' function
1158 * Get the headers of the results table, for all of the columns
1160 * @param array $displayParts which elements to display
1161 * @param array $analyzed_sql_results analyzed sql results
1162 * @param array $sort_expression sort expression
1163 * @param array $sort_expression_nodirection sort expression
1165 * @param array $sort_direction sort direction
1166 * @param boolean $is_limited_display with limited operations
1168 * @param string $unsorted_sql_query query without the sort part
1170 * @return string html content
1174 * @see getTableHeaders()
1176 private function _getTableHeadersForColumns(
1177 $displayParts, $analyzed_sql_results, $sort_expression,
1178 $sort_expression_nodirection, $sort_direction, $is_limited_display,
1183 // required to generate sort links that will remember whether the
1184 // "Show all" button has been clicked
1185 $sql_md5 = md5($this->__get('sql_query'));
1186 $session_max_rows = $is_limited_display
1188 : $_SESSION['tmpval']['query'][$sql_md5]['max_rows'];
1190 // Following variable are needed for use in isset/empty or
1191 // use with array indexes/safe use in the for loop
1192 $highlight_columns = $this->__get('highlight_columns');
1193 $fields_meta = $this->__get('fields_meta');
1195 // Prepare Display column comments if enabled
1196 // ($GLOBALS['cfg']['ShowBrowseComments']).
1197 $comments_map = $this->_getTableCommentsArray($analyzed_sql_results);
1199 list($col_order, $col_visib) = $this->_getColumnParams(
1200 $analyzed_sql_results
1203 // optimize: avoid calling a method on each iteration
1204 $number_of_columns = $this->__get('fields_cnt');
1206 for ($j = 0; $j < $number_of_columns; $j++
) {
1208 // assign $i with the appropriate column order
1209 $i = $col_order ?
$col_order[$j] : $j;
1211 // See if this column should get highlight because it's used in the
1213 $name = $fields_meta[$i]->name
;
1214 $condition_field = (isset($highlight_columns[$name])
1215 ||
isset($highlight_columns[Util
::backquote($name)]))
1219 // Prepare comment-HTML-wrappers for each row, if defined/enabled.
1220 $comments = $this->_getCommentForRow($comments_map, $fields_meta[$i]);
1221 $display_params = $this->__get('display_params');
1223 if (($displayParts['sort_lnk'] == '1') && ! $is_limited_display) {
1225 list($order_link, $sorted_header_html)
1226 = $this->_getOrderLinkAndSortedHeaderHtml(
1227 $fields_meta[$i], $sort_expression,
1228 $sort_expression_nodirection, $i, $unsorted_sql_query,
1229 $session_max_rows, $comments,
1230 $sort_direction, $col_visib,
1234 $html .= $sorted_header_html;
1236 $display_params['desc'][] = ' <th '
1237 . 'class="draggable'
1238 . ($condition_field ?
' condition' : '')
1239 . '" data-column="' . htmlspecialchars($fields_meta[$i]->name
)
1240 . '">' . "\n" . $order_link . $comments . ' </th>' . "\n";
1242 // Results can't be sorted
1244 .= $this->_getDraggableClassForNonSortableColumns(
1245 $col_visib, $col_visib[$j], $condition_field,
1246 $fields_meta[$i], $comments
1249 $display_params['desc'][] = ' <th '
1250 . 'class="draggable'
1251 . ($condition_field ?
' condition"' : '')
1252 . '" data-column="' . htmlspecialchars($fields_meta[$i]->name
)
1254 . htmlspecialchars($fields_meta[$i]->name
)
1255 . $comments . ' </th>';
1258 $this->__set('display_params', $display_params);
1265 * Get the headers of the results table
1267 * @param array &$displayParts which elements to display
1268 * @param array $analyzed_sql_results analyzed sql results
1269 * @param string $unsorted_sql_query the unsorted sql query
1270 * @param array $sort_expression sort expression
1271 * @param array $sort_expression_nodirection sort expression
1273 * @param array $sort_direction sort direction
1274 * @param boolean $is_limited_display with limited operations
1277 * @return string html content
1283 private function _getTableHeaders(
1284 &$displayParts, $analyzed_sql_results, $unsorted_sql_query,
1285 $sort_expression = array(), $sort_expression_nodirection = '',
1286 $sort_direction = array(), $is_limited_display = false
1289 $table_headers_html = '';
1290 // Needed for use in isset/empty or
1291 // use with array indexes/safe use in foreach
1292 $printview = $this->__get('printview');
1293 $display_params = $this->__get('display_params');
1295 // Output data needed for grid editing
1296 $table_headers_html .= '<input class="save_cells_at_once" type="hidden"'
1297 . ' value="' . $GLOBALS['cfg']['SaveCellsAtOnce'] . '" />'
1298 . '<div class="common_hidden_inputs">'
1299 . URL
::getHiddenInputs(
1300 $this->__get('db'), $this->__get('table')
1304 // Output data needed for column reordering and show/hide column
1305 if ($this->_isSelect($analyzed_sql_results)) {
1306 $table_headers_html .= $this->_getDataForResettingColumnOrder();
1309 $display_params['emptypre'] = 0;
1310 $display_params['emptyafter'] = 0;
1311 $display_params['textbtn'] = '';
1312 $full_or_partial_text_link = null;
1314 $this->__set('display_params', $display_params);
1316 // Display options (if we are not in print view)
1317 if (! (isset($printview) && ($printview == '1')) && ! $is_limited_display) {
1319 $table_headers_html .= $this->_getOptionsBlock();
1321 // prepare full/partial text button or link
1322 $full_or_partial_text_link = $this->_getFullOrPartialTextButtonOrLink();
1325 // Start of form for multi-rows edit/delete/export
1326 $table_headers_html .= $this->_getFormForMultiRowOperations(
1327 $displayParts['del_lnk']
1330 // 1. Set $colspan and generate html with full/partial
1331 // text button or link
1332 list($colspan, $button_html)
1333 = $this->_getFieldVisibilityParams(
1334 $displayParts, $full_or_partial_text_link
1337 $table_headers_html .= $button_html;
1339 // 2. Displays the fields' name
1340 // 2.0 If sorting links should be used, checks if the query is a "JOIN"
1341 // statement (see 2.1.3)
1343 // See if we have to highlight any header fields of a WHERE query.
1344 // Uses SQL-Parser results.
1345 $this->_setHighlightedColumnGlobalField($analyzed_sql_results);
1347 // Get the headers for all of the columns
1348 $table_headers_html .= $this->_getTableHeadersForColumns(
1349 $displayParts, $analyzed_sql_results, $sort_expression,
1350 $sort_expression_nodirection, $sort_direction,
1351 $is_limited_display, $unsorted_sql_query
1354 // Display column at rightside - checkboxes or empty column
1356 $table_headers_html .= $this->_getColumnAtRightSide(
1357 $displayParts, $full_or_partial_text_link, $colspan
1360 $table_headers_html .= '</tr>' . '</thead>';
1362 return $table_headers_html;
1364 } // end of the '_getTableHeaders()' function
1368 * Prepare unsorted sql query and sort by key drop down
1370 * @param array $analyzed_sql_results analyzed sql results
1371 * @param string $sort_expression sort expression
1373 * @return array two element array - $unsorted_sql_query, $drop_down_html
1377 * @see _getTableHeaders()
1379 private function _getUnsortedSqlAndSortByKeyDropDown(
1380 $analyzed_sql_results, $sort_expression
1382 $drop_down_html = '';
1384 $unsorted_sql_query = Query
::replaceClause(
1385 $analyzed_sql_results['statement'],
1386 $analyzed_sql_results['parser']->list,
1391 // Data is sorted by indexes only if it there is only one table.
1392 if ($this->_isSelect($analyzed_sql_results)) {
1393 // grab indexes data:
1394 $indexes = Index
::getFromTable(
1395 $this->__get('table'),
1399 // do we have any index?
1400 if (! empty($indexes)) {
1401 $drop_down_html = $this->_getSortByKeyDropDown(
1402 $indexes, $sort_expression,
1408 return array($unsorted_sql_query, $drop_down_html);
1410 } // end of the '_getUnsortedSqlAndSortByKeyDropDown()' function
1413 * Prepare sort by key dropdown - html code segment
1415 * @param Index[] $indexes the indexes of the table for sort criteria
1416 * @param string $sort_expression the sort expression
1417 * @param string $unsorted_sql_query the unsorted sql query
1419 * @return string $drop_down_html html content
1423 * @see _getTableHeaders()
1425 private function _getSortByKeyDropDown(
1426 $indexes, $sort_expression, $unsorted_sql_query
1429 $drop_down_html = '';
1431 $drop_down_html .= '<form action="sql.php" method="post" ' .
1432 'class="print_ignore">' . "\n"
1433 . URL
::getHiddenInputs(
1434 $this->__get('db'), $this->__get('table')
1436 // to avoid calling PMA_handleSortOrder() later
1437 . URL
::getHiddenFields(array('sort_by_key' => '1'))
1439 . ': <select name="sql_query" class="autosubmit">' . "\n";
1441 $used_index = false;
1442 $local_order = (isset($sort_expression) ?
$sort_expression : '');
1444 foreach ($indexes as $index) {
1447 . implode('` ASC, `', array_keys($index->getColumns()))
1451 . implode('` DESC, `', array_keys($index->getColumns()))
1454 $used_index = $used_index
1455 ||
($local_order == $asc_sort)
1456 ||
($local_order == $desc_sort);
1459 '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|'
1460 . 'FOR UPDATE|LOCK IN SHARE MODE))@is',
1461 $unsorted_sql_query, $my_reg
1463 $unsorted_sql_query_first_part = $my_reg[1];
1464 $unsorted_sql_query_second_part = $my_reg[2];
1466 $unsorted_sql_query_first_part = $unsorted_sql_query;
1467 $unsorted_sql_query_second_part = '';
1470 $drop_down_html .= '<option value="'
1472 $unsorted_sql_query_first_part . "\n"
1473 . ' ORDER BY ' . $asc_sort
1474 . $unsorted_sql_query_second_part
1476 . '"' . ($local_order == $asc_sort
1477 ?
' selected="selected"'
1479 . '>' . htmlspecialchars($index->getName()) . ' (ASC)</option>';
1481 $drop_down_html .= '<option value="'
1483 $unsorted_sql_query_first_part . "\n"
1484 . ' ORDER BY ' . $desc_sort
1485 . $unsorted_sql_query_second_part
1487 . '"' . ($local_order == $desc_sort
1488 ?
' selected="selected"'
1490 . '>' . htmlspecialchars($index->getName()) . ' (DESC)</option>';
1493 $drop_down_html .= '<option value="' . htmlspecialchars($unsorted_sql_query)
1494 . '"' . ($used_index ?
'' : ' selected="selected"') . '>' . __('None')
1496 . '</select>' . "\n"
1499 return $drop_down_html;
1501 } // end of the '_getSortByKeyDropDown()' function
1505 * Set column span, row span and prepare html with full/partial
1506 * text button or link
1508 * @param array &$displayParts which elements to display
1509 * @param string $full_or_partial_text_link full/partial link or text button
1511 * @return array 2 element array - $colspan, $button_html
1515 * @see _getTableHeaders()
1517 private function _getFieldVisibilityParams(
1518 &$displayParts, $full_or_partial_text_link
1522 $display_params = $this->__get('display_params');
1524 // 1. Displays the full/partial text button (part 1)...
1525 $button_html .= '<thead><tr>' . "\n";
1527 $colspan = (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
1528 && ($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
))
1532 // ... before the result table
1533 if ((($displayParts['edit_lnk'] == self
::NO_EDIT_OR_DELETE
)
1534 && ($displayParts['del_lnk'] == self
::NO_EDIT_OR_DELETE
))
1535 && ($displayParts['text_btn'] == '1')
1538 $display_params['emptypre']
1539 = (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
1540 && ($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)) ?
4 : 0;
1542 } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_LEFT
)
1543 ||
($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_BOTH
))
1544 && ($displayParts['text_btn'] == '1')
1546 // ... at the left column of the result table header if possible
1549 $display_params['emptypre']
1550 = (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
1551 && ($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)) ?
4 : 0;
1553 $button_html .= '<th class="column_action print_ignore" ' . $colspan
1554 . '>' . $full_or_partial_text_link . '</th>';
1556 } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_LEFT
)
1557 ||
($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_BOTH
))
1558 && (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
1559 ||
($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
))
1561 // ... elseif no button, displays empty(ies) col(s) if required
1563 $display_params['emptypre']
1564 = (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
1565 && ($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)) ?
4 : 0;
1567 $button_html .= '<td ' . $colspan . '></td>';
1569 } elseif (($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_NONE
)) {
1570 // ... elseif display an empty column if the actions links are
1571 // disabled to match the rest of the table
1572 $button_html .= '<th class="column_action"></th>';
1575 $this->__set('display_params', $display_params);
1577 return array($colspan, $button_html);
1579 } // end of the '_getFieldVisibilityParams()' function
1583 * Get table comments as array
1585 * @param array $analyzed_sql_results analyzed sql results
1587 * @return array $comments_map table comments
1591 * @see _getTableHeaders()
1593 private function _getTableCommentsArray($analyzed_sql_results)
1595 if ((!$GLOBALS['cfg']['ShowBrowseComments'])
1596 ||
(empty($analyzed_sql_results['statement']->from
))
1602 foreach ($analyzed_sql_results['statement']->from
as $field) {
1603 if (empty($field->table
)) {
1606 $ret[$field->table
] = PMA_getComments(
1607 empty($field->database
) ?
$this->__get('db') : $field->database
,
1614 } // end of the '_getTableCommentsArray()' function
1618 * Set global array for store highlighted header fields
1620 * @param array $analyzed_sql_results analyzed sql results
1626 * @see _getTableHeaders()
1628 private function _setHighlightedColumnGlobalField($analyzed_sql_results)
1630 $highlight_columns = array();
1632 if (!empty($analyzed_sql_results['statement']->where
)) {
1633 foreach ($analyzed_sql_results['statement']->where
as $expr) {
1634 foreach ($expr->identifiers
as $identifier) {
1635 $highlight_columns[$identifier] = 'true';
1640 $this->__set('highlight_columns', $highlight_columns);
1642 } // end of the '_setHighlightedColumnGlobalField()' function
1646 * Prepare data for column restoring and show/hide
1648 * @return string $data_html html content
1652 * @see _getTableHeaders()
1654 private function _getDataForResettingColumnOrder()
1659 // generate the column order, if it is set
1660 $pmatable = new Table($this->__get('table'), $this->__get('db'));
1661 $col_order = $pmatable->getUiProp(Table
::PROP_COLUMN_ORDER
);
1664 $data_html .= '<input class="col_order" type="hidden" value="'
1665 . implode(',', $col_order) . '" />';
1668 $col_visib = $pmatable->getUiProp(Table
::PROP_COLUMN_VISIB
);
1671 $data_html .= '<input class="col_visib" type="hidden" value="'
1672 . implode(',', $col_visib) . '" />';
1675 // generate table create time
1676 $table = new Table($this->__get('table'), $this->__get('db'));
1677 if (! $table->isView()) {
1678 $data_html .= '<input class="table_create_time" type="hidden" value="'
1679 . $GLOBALS['dbi']->getTable(
1680 $this->__get('db'), $this->__get('table')
1681 )->getStatusInfo('Create_time')
1687 } // end of the '_getDataForResettingColumnOrder()' function
1691 * Prepare option fields block
1693 * @return string $options_html html content
1697 * @see _getTableHeaders()
1699 private function _getOptionsBlock()
1704 $options_html .= '<form method="post" action="sql.php" '
1705 . 'name="displayOptionsForm"';
1707 $options_html .= ' class="ajax print_ignore" ';
1709 $options_html .= '>';
1710 $url_params = array(
1711 'db' => $this->__get('db'),
1712 'table' => $this->__get('table'),
1713 'sql_query' => $this->__get('sql_query'),
1714 'goto' => $this->__get('goto'),
1715 'display_options_form' => 1
1718 $options_html .= URL
::getHiddenInputs($url_params)
1720 . Util
::getDivForSliderEffect(
1725 $options_html .= '<div class="formelement">';
1727 'P' => __('Partial texts'),
1728 'F' => __('Full texts')
1731 // pftext means "partial or full texts" (done to reduce line lengths)
1732 $options_html .= Util
::getRadioFields(
1734 $_SESSION['tmpval']['pftext'],
1735 true, true, '', 'pftext_' . $this->__get('unique_id')
1739 if ($GLOBALS['cfgRelation']['relwork']
1740 && $GLOBALS['cfgRelation']['displaywork']
1742 $options_html .= '<div class="formelement">';
1744 'K' => __('Relational key'),
1745 'D' => __('Display column for relationships')
1748 $options_html .= Util
::getRadioFields(
1749 'relational_display', $choices,
1750 $_SESSION['tmpval']['relational_display'],
1751 true, true, '', 'relational_display_' . $this->__get('unique_id')
1756 $options_html .= '<div class="formelement">'
1757 . Template
::get('checkbox')
1760 'html_field_name' => 'display_binary',
1761 'label' => __('Show binary contents'),
1762 'checked' => ! empty($_SESSION['tmpval']['display_binary']),
1764 'html_field_id' => 'display_binary_' . $this->__get('unique_id'),
1768 . Template
::get('checkbox')
1771 'html_field_name' => 'display_blob',
1772 'label' => __('Show BLOB contents'),
1773 'checked' => ! empty($_SESSION['tmpval']['display_blob']),
1775 'html_field_id' => 'display_blob_' . $this->__get('unique_id'),
1780 // I would have preferred to name this "display_transformation".
1781 // This is the only way I found to be able to keep this setting sticky
1782 // per SQL query, and at the same time have a default that displays
1783 // the transformations.
1784 $options_html .= '<div class="formelement">'
1785 . Template
::get('checkbox')
1788 'html_field_name' => 'hide_transformation',
1789 'label' => __('Hide browser transformation'),
1790 'checked' => ! empty($_SESSION['tmpval']['hide_transformation']),
1792 'html_field_id' => 'hide_transformation_' . $this->__get('unique_id'),
1797 $options_html .= '<div class="formelement">';
1799 'GEOM' => __('Geometry'),
1800 'WKT' => __('Well Known Text'),
1801 'WKB' => __('Well Known Binary')
1804 $options_html .= Util
::getRadioFields(
1805 'geoOption', $choices,
1806 $_SESSION['tmpval']['geoOption'],
1807 true, true, '', 'geoOption_' . $this->__get('unique_id')
1809 $options_html .= '</div>';
1811 $options_html .= '<div class="clearfloat"></div>'
1814 $options_html .= '<fieldset class="tblFooters">'
1815 . '<input type="submit" value="' . __('Go') . '" />'
1820 return $options_html;
1822 } // end of the '_getOptionsBlock()' function
1826 * Get full/partial text button or link
1828 * @return string html content
1832 * @see _getTableHeaders()
1834 private function _getFullOrPartialTextButtonOrLink()
1837 $url_params_full_text = array(
1838 'db' => $this->__get('db'),
1839 'table' => $this->__get('table'),
1840 'sql_query' => $this->__get('sql_query'),
1841 'goto' => $this->__get('goto'),
1842 'full_text_button' => 1
1845 if ($_SESSION['tmpval']['pftext'] == self
::DISPLAY_FULL_TEXT
) {
1846 // currently in fulltext mode so show the opposite link
1847 $tmp_image_file = $this->__get('pma_theme_image') . 's_partialtext.png';
1848 $tmp_txt = __('Partial texts');
1849 $url_params_full_text['pftext'] = self
::DISPLAY_PARTIAL_TEXT
;
1851 $tmp_image_file = $this->__get('pma_theme_image') . 's_fulltext.png';
1852 $tmp_txt = __('Full texts');
1853 $url_params_full_text['pftext'] = self
::DISPLAY_FULL_TEXT
;
1856 $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="'
1857 . $tmp_txt . '" title="' . $tmp_txt . '" />';
1858 $tmp_url = 'sql.php' . URL
::getCommon($url_params_full_text);
1860 return Util
::linkOrButton(
1861 $tmp_url, $tmp_image, array(), false
1864 } // end of the '_getFullOrPartialTextButtonOrLink()' function
1868 * Prepare html form for multi row operations
1870 * @param string $del_lnk the delete link of current row
1872 * @return string $form_html html content
1876 * @see _getTableHeaders()
1878 private function _getFormForMultiRowOperations($del_lnk)
1883 if (($del_lnk == self
::DELETE_ROW
) ||
($del_lnk == self
::KILL_PROCESS
)) {
1885 $form_html .= '<form method="post" action="tbl_row_action.php" '
1886 . 'name="resultsForm"'
1887 . ' id="resultsForm_' . $this->__get('unique_id') . '"';
1889 $form_html .= ' class="ajax" ';
1892 . URL
::getHiddenInputs(
1893 $this->__get('db'), $this->__get('table'), 1
1895 . '<input type="hidden" name="goto" value="sql.php" />';
1898 $form_html .= '<table class="table_results data ajax"';
1899 $form_html .= ' data-uniqueId="' . $this->__get('unique_id') . '"';
1904 } // end of the '_getFormForMultiRowOperations()' function
1908 * Get comment for row
1910 * @param array $comments_map comments array
1911 * @param array $fields_meta set of field properties
1913 * @return string $comment html content
1917 * @see _getTableHeaders()
1919 private function _getCommentForRow($comments_map, $fields_meta)
1922 if (isset($comments_map[$fields_meta->table
])
1923 && isset($comments_map[$fields_meta->table
][$fields_meta->name
])
1925 $sanitized_comments = htmlspecialchars(
1926 $comments_map[$fields_meta->table
][$fields_meta->name
]
1929 $comments = '<span class="tblcomment" title="'
1930 . $sanitized_comments . '">';
1931 $limitChars = $GLOBALS['cfg']['LimitChars'];
1932 if (mb_strlen($sanitized_comments) > $limitChars) {
1933 $sanitized_comments = mb_substr(
1934 $sanitized_comments, 0, $limitChars
1937 $comments .= $sanitized_comments;
1938 $comments .= '</span>';
1941 } // end of the '_getCommentForRow()' function
1945 * Prepare parameters and html for sorted table header fields
1947 * @param array $fields_meta set of field properties
1948 * @param array $sort_expression sort expression
1949 * @param array $sort_expression_nodirection sort expression without direction
1950 * @param integer $column_index the index of the column
1951 * @param string $unsorted_sql_query the unsorted sql query
1952 * @param integer $session_max_rows maximum rows resulted by sql
1953 * @param string $comments comment for row
1954 * @param array $sort_direction sort direction
1955 * @param boolean $col_visib column is visible(false)
1956 * array column isn't visible(string array)
1957 * @param string $col_visib_j element of $col_visib array
1959 * @return array 2 element array - $order_link, $sorted_header_html
1963 * @see _getTableHeaders()
1965 private function _getOrderLinkAndSortedHeaderHtml(
1966 $fields_meta, $sort_expression, $sort_expression_nodirection,
1967 $column_index, $unsorted_sql_query, $session_max_rows,
1968 $comments, $sort_direction, $col_visib, $col_visib_j
1971 $sorted_header_html = '';
1973 // Checks if the table name is required; it's the case
1974 // for a query with a "JOIN" statement and if the column
1975 // isn't aliased, or in queries like
1976 // SELECT `1`.`master_field` , `2`.`master_field`
1977 // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2`
1979 $sort_tbl = (isset($fields_meta->table
)
1980 && strlen($fields_meta->table
) > 0
1981 && $fields_meta->orgname
== $fields_meta->name
)
1987 $name_to_use_in_sort = $fields_meta->name
;
1989 // Generates the orderby clause part of the query which is part
1991 list($single_sort_order, $multi_sort_order, $order_img)
1992 = $this->_getSingleAndMultiSortUrls(
1993 $sort_expression, $sort_expression_nodirection, $sort_tbl,
1994 $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index
1998 '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|'
1999 . 'LOCK IN SHARE MODE))@is',
2000 $unsorted_sql_query, $regs3
2002 $single_sorted_sql_query = $regs3[1] . $single_sort_order . $regs3[2];
2003 $multi_sorted_sql_query = $regs3[1] . $multi_sort_order . $regs3[2];
2005 $single_sorted_sql_query = $unsorted_sql_query . $single_sort_order;
2006 $multi_sorted_sql_query = $unsorted_sql_query . $multi_sort_order;
2009 $_single_url_params = array(
2010 'db' => $this->__get('db'),
2011 'table' => $this->__get('table'),
2012 'sql_query' => $single_sorted_sql_query,
2013 'session_max_rows' => $session_max_rows,
2014 'is_browse_distinct' => $this->__get('is_browse_distinct'),
2017 $_multi_url_params = array(
2018 'db' => $this->__get('db'),
2019 'table' => $this->__get('table'),
2020 'sql_query' => $multi_sorted_sql_query,
2021 'session_max_rows' => $session_max_rows,
2022 'is_browse_distinct' => $this->__get('is_browse_distinct'),
2024 $single_order_url = 'sql.php' . URL
::getCommon($_single_url_params);
2025 $multi_order_url = 'sql.php' . URL
::getCommon($_multi_url_params);
2027 // Displays the sorting URL
2028 // enable sort order swapping for image
2029 $order_link = $this->_getSortOrderLink(
2030 $order_img, $column_index,
2031 $fields_meta, $single_order_url, $multi_order_url
2034 $sorted_header_html .= $this->_getDraggableClassForSortableColumns(
2035 $col_visib, $col_visib_j,
2036 $fields_meta, $order_link, $comments
2039 return array($order_link, $sorted_header_html);
2041 } // end of the '_getOrderLinkAndSortedHeaderHtml()' function
2044 * Prepare parameters and html for sorted table header fields
2046 * @param array $sort_expression sort expression
2047 * @param array $sort_expression_nodirection sort expression without direction
2048 * @param string $sort_tbl The name of the table to which
2049 * the current column belongs to
2050 * @param string $name_to_use_in_sort The current column under
2052 * @param array $sort_direction sort direction
2053 * @param array $fields_meta set of field properties
2054 * @param integer $column_index The index number to current column
2056 * @return array 3 element array - $single_sort_order, $sort_order, $order_img
2060 * @see _getOrderLinkAndSortedHeaderHtml()
2062 private function _getSingleAndMultiSortUrls(
2063 $sort_expression, $sort_expression_nodirection, $sort_tbl,
2064 $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index
2067 // Check if the current column is in the order by clause
2068 $is_in_sort = $this->_isInSorted(
2069 $sort_expression, $sort_expression_nodirection,
2070 $sort_tbl, $name_to_use_in_sort
2072 $current_name = $name_to_use_in_sort;
2073 if ($sort_expression_nodirection[0] == '' ||
!$is_in_sort) {
2074 $special_index = $sort_expression_nodirection[0] == ''
2076 : count($sort_expression_nodirection);
2077 $sort_expression_nodirection[$special_index]
2081 $sort_direction[$special_index] = (preg_match(
2084 )) ? self
::DESCENDING_SORT_DIR
: self
::ASCENDING_SORT_DIR
;
2088 $sort_expression_nodirection = array_filter($sort_expression_nodirection);
2089 $single_sort_order = null;
2090 foreach ($sort_expression_nodirection as $index=>$expression) {
2091 // check if this is the first clause,
2092 // if it is then we have to add "order by"
2093 $is_first_clause = ($index == 0);
2094 $name_to_use_in_sort = $expression;
2095 $sort_tbl_new = $sort_tbl;
2096 // Test to detect if the column name is a standard name
2097 // Standard name has the table name prefixed to the column name
2098 if (mb_strpos($name_to_use_in_sort, '.') !== false) {
2099 $matches = explode('.', $name_to_use_in_sort);
2100 // Matches[0] has the table name
2101 // Matches[1] has the column name
2102 $name_to_use_in_sort = $matches[1];
2103 $sort_tbl_new = $matches[0];
2106 // $name_to_use_in_sort might contain a space due to
2107 // formatting of function expressions like "COUNT(name )"
2108 // so we remove the space in this situation
2109 $name_to_use_in_sort = str_replace(' )', ')', $name_to_use_in_sort);
2110 $name_to_use_in_sort = str_replace('``', '`', $name_to_use_in_sort);
2111 $name_to_use_in_sort = trim($name_to_use_in_sort, '`');
2113 // If this the first column name in the order by clause add
2114 // order by clause to the column name
2115 $query_head = $is_first_clause ?
"\nORDER BY " : "";
2116 // Again a check to see if the given column is a aggregate column
2117 if (mb_strpos($name_to_use_in_sort, '(') !== false) {
2118 $sort_order .= $query_head . $name_to_use_in_sort . ' ' ;
2120 if (strlen($sort_tbl_new) > 0) {
2121 $sort_tbl_new .= ".";
2123 $sort_order .= $query_head . $sort_tbl_new
2125 $name_to_use_in_sort
2129 // For a special case where the code generates two dots between
2130 // column name and table name.
2131 $sort_order = preg_replace("/\.\./", ".", $sort_order);
2132 // Incase this is the current column save $single_sort_order
2133 if ($current_name == $name_to_use_in_sort) {
2134 if (mb_strpos($current_name, '(') !== false) {
2135 $single_sort_order = "\n" . 'ORDER BY ' . $current_name . ' ';
2137 $single_sort_order = "\n" . 'ORDER BY ' . $sort_tbl
2143 list($single_sort_order, $order_img)
2144 = $this->_getSortingUrlParams(
2145 $sort_direction, $single_sort_order,
2146 $column_index, $index
2149 $single_sort_order .= strtoupper($sort_direction[$index]);
2152 if ($current_name == $name_to_use_in_sort && $is_in_sort) {
2153 // We need to generate the arrow button and related html
2154 list($sort_order, $order_img) = $this->_getSortingUrlParams(
2155 $sort_direction, $sort_order, $column_index, $index
2157 $order_img .= " <small>" . ($index +
1) . "</small>";
2159 $sort_order .= strtoupper($sort_direction[$index]);
2161 // Separate columns by a comma
2162 $sort_order .= ", ";
2164 unset($name_to_use_in_sort);
2166 // remove the comma from the last column name in the newly
2167 // constructed clause
2168 $sort_order = mb_substr(
2171 mb_strlen($sort_order) - 2
2173 if (empty($order_img)) {
2176 return array($single_sort_order, $sort_order, $order_img);
2180 * Check whether the column is sorted
2182 * @param array $sort_expression sort expression
2183 * @param array $sort_expression_nodirection sort expression without direction
2184 * @param string $sort_tbl the table name
2185 * @param string $name_to_use_in_sort the sorting column name
2187 * @return boolean $is_in_sort the column sorted or not
2191 * @see _getTableHeaders()
2193 private function _isInSorted(
2194 $sort_expression, $sort_expression_nodirection, $sort_tbl,
2195 $name_to_use_in_sort
2198 $index_in_expression = 0;
2200 foreach ($sort_expression_nodirection as $index => $clause) {
2201 if (mb_strpos($clause, '.') !== false) {
2202 $fragments = explode('.', $clause);
2203 $clause2 = $fragments[0] . "." . str_replace('`', '', $fragments[1]);
2205 $clause2 = $sort_tbl . str_replace('`', '', $clause);
2207 if ($clause2 === $sort_tbl . $name_to_use_in_sort) {
2208 $index_in_expression = $index;
2212 if (empty($sort_expression[$index_in_expression])) {
2213 $is_in_sort = false;
2215 // Field name may be preceded by a space, or any number
2216 // of characters followed by a dot (tablename.fieldname)
2217 // so do a direct comparison for the sort expression;
2218 // this avoids problems with queries like
2219 // "SELECT id, count(id)..." and clicking to sort
2220 // on id or on count(id).
2221 // Another query to test this:
2222 // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p
2223 // (and try clicking on each column's header twice)
2224 $noSortTable = empty($sort_tbl) ||
mb_strpos(
2225 $sort_expression_nodirection[$index_in_expression], $sort_tbl
2227 $noOpenParenthesis = mb_strpos(
2228 $sort_expression_nodirection[$index_in_expression], '('
2230 if (! empty($sort_tbl) && $noSortTable && $noOpenParenthesis) {
2231 $new_sort_expression_nodirection = $sort_tbl
2232 . $sort_expression_nodirection[$index_in_expression];
2234 $new_sort_expression_nodirection
2235 = $sort_expression_nodirection[$index_in_expression];
2238 //Back quotes are removed in next comparison, so remove them from value
2240 $name_to_use_in_sort = str_replace('`', '', $name_to_use_in_sort);
2242 $is_in_sort = false;
2243 $sort_name = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort;
2245 if ($sort_name == str_replace('`', '', $new_sort_expression_nodirection)
2246 ||
$sort_name == str_replace('`', '', $sort_expression_nodirection[$index_in_expression])
2254 } // end of the '_isInSorted()' function
2258 * Get sort url parameters - sort order and order image
2260 * @param array $sort_direction the sort direction
2261 * @param string $sort_order the sorting order
2262 * @param integer $column_index the index of the column
2263 * @param integer $index the index of sort direction array.
2265 * @return array 2 element array - $sort_order, $order_img
2269 * @see _getSingleAndMultiSortUrls()
2271 private function _getSortingUrlParams(
2272 $sort_direction, $sort_order, $column_index, $index
2274 if (strtoupper(trim($sort_direction[$index])) == self
::DESCENDING_SORT_DIR
) {
2275 $sort_order .= ' ASC';
2276 $order_img = ' ' . Util
::getImage(
2277 's_desc.png', __('Descending'),
2278 array('class' => "soimg$column_index", 'title' => '')
2280 $order_img .= ' ' . Util
::getImage(
2281 's_asc.png', __('Ascending'),
2282 array('class' => "soimg$column_index hide", 'title' => '')
2285 $sort_order .= ' DESC';
2286 $order_img = ' ' . Util
::getImage(
2287 's_asc.png', __('Ascending'),
2288 array('class' => "soimg$column_index", 'title' => '')
2290 $order_img .= ' ' . Util
::getImage(
2291 's_desc.png', __('Descending'),
2292 array('class' => "soimg$column_index hide", 'title' => '')
2295 return array($sort_order, $order_img);
2296 } // end of the '_getSortingUrlParams()' function
2300 * Get sort order link
2302 * @param string $order_img the sort order image
2303 * @param integer $col_index the index of the column
2304 * @param array $fields_meta set of field properties
2305 * @param string $order_url the url for sort
2306 * @param string $multi_order_url the url for sort
2308 * @return string the sort order link
2312 * @see _getTableHeaders()
2314 private function _getSortOrderLink(
2315 $order_img, $col_index,
2316 $fields_meta, $order_url, $multi_order_url
2318 $order_link_params = array();
2319 if (isset($order_img) && ($order_img != '')) {
2320 if (mb_strstr($order_img, 'asc')) {
2321 $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()";
2322 $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()";
2323 } elseif (mb_strstr($order_img, 'desc')) {
2324 $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()";
2325 $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()";
2329 $order_link_content = htmlspecialchars($fields_meta->name
);
2330 $inner_link_content = $order_link_content . $order_img
2331 . '<input type="hidden" value="' . $multi_order_url . '" />';
2333 return Util
::linkOrButton(
2334 $order_url, $inner_link_content,
2335 $order_link_params, false, true
2338 } // end of the '_getSortOrderLink()' function
2341 * Check if the column contains numeric data. If yes, then set the
2342 * column header's alignment right
2344 * @param array $fields_meta set of field properties
2345 * @param array &$th_class array containing classes
2349 * @see _getDraggableClassForSortableColumns()
2351 private function _getClassForNumericColumnType($fields_meta,&$th_class)
2354 '@int|decimal|float|double|real|bit|boolean|serial@i',
2357 $th_class[] = 'right';
2362 * Prepare columns to draggable effect for sortable columns
2364 * @param boolean $col_visib the column is visible (false)
2365 * array the column is not visible (string array)
2366 * @param string $col_visib_j element of $col_visib array
2367 * @param array $fields_meta set of field properties
2368 * @param string $order_link the order link
2369 * @param string $comments the comment for the column
2371 * @return string $draggable_html html content
2375 * @see _getTableHeaders()
2377 private function _getDraggableClassForSortableColumns(
2378 $col_visib, $col_visib_j, $fields_meta,
2379 $order_link, $comments
2382 $draggable_html = '<th';
2383 $th_class = array();
2384 $th_class[] = 'draggable';
2385 $this->_getClassForNumericColumnType($fields_meta, $th_class);
2386 if ($col_visib && !$col_visib_j) {
2387 $th_class[] = 'hide';
2390 $th_class[] = 'column_heading';
2391 if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) {
2392 $th_class[] = 'pointer';
2395 if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) {
2396 $th_class[] = 'marker';
2399 $draggable_html .= ' class="' . implode(' ', $th_class) . '"';
2401 $draggable_html .= ' data-column="' . htmlspecialchars($fields_meta->name
)
2402 . '">' . $order_link . $comments . '</th>';
2404 return $draggable_html;
2406 } // end of the '_getDraggableClassForSortableColumns()' function
2410 * Prepare columns to draggable effect for non sortable columns
2412 * @param boolean $col_visib the column is visible (false)
2413 * array the column is not visible (string array)
2414 * @param string $col_visib_j element of $col_visib array
2415 * @param boolean $condition_field whether to add CSS class condition
2416 * @param array $fields_meta set of field properties
2417 * @param string $comments the comment for the column
2419 * @return string $draggable_html html content
2423 * @see _getTableHeaders()
2425 private function _getDraggableClassForNonSortableColumns(
2426 $col_visib, $col_visib_j, $condition_field,
2427 $fields_meta, $comments
2430 $draggable_html = '<th';
2431 $th_class = array();
2432 $th_class[] = 'draggable';
2433 $this->_getClassForNumericColumnType($fields_meta, $th_class);
2434 if ($col_visib && !$col_visib_j) {
2435 $th_class[] = 'hide';
2438 if ($condition_field) {
2439 $th_class[] = 'condition';
2442 $draggable_html .= ' class="' . implode(' ', $th_class) . '"';
2444 $draggable_html .= ' data-column="'
2445 . htmlspecialchars($fields_meta->name
) . '">';
2447 $draggable_html .= htmlspecialchars($fields_meta->name
);
2449 $draggable_html .= "\n" . $comments . '</th>';
2451 return $draggable_html;
2453 } // end of the '_getDraggableClassForNonSortableColumns()' function
2457 * Prepare column to show at right side - check boxes or empty column
2459 * @param array &$displayParts which elements to display
2460 * @param string $full_or_partial_text_link full/partial link or text button
2461 * @param string $colspan column span of table header
2463 * @return string html content
2467 * @see _getTableHeaders()
2469 private function _getColumnAtRightSide(
2470 &$displayParts, $full_or_partial_text_link, $colspan
2473 $right_column_html = '';
2474 $display_params = $this->__get('display_params');
2476 // Displays the needed checkboxes at the right
2477 // column of the result table header if possible and required...
2478 if ((($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_RIGHT
)
2479 ||
($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_BOTH
))
2480 && (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
2481 ||
($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
))
2482 && ($displayParts['text_btn'] == '1')
2485 $display_params['emptyafter']
2486 = (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
2487 && ($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)) ?
4 : 1;
2489 $right_column_html .= "\n"
2490 . '<th class="column_action print_ignore" ' . $colspan . '>'
2491 . $full_or_partial_text_link
2494 } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_LEFT
)
2495 ||
($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_BOTH
))
2496 && (($displayParts['edit_lnk'] == self
::NO_EDIT_OR_DELETE
)
2497 && ($displayParts['del_lnk'] == self
::NO_EDIT_OR_DELETE
))
2498 && (! isset($GLOBALS['is_header_sent']) ||
! $GLOBALS['is_header_sent'])
2500 // ... elseif no button, displays empty columns if required
2501 // (unless coming from Browse mode print view)
2503 $display_params['emptyafter']
2504 = (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
2505 && ($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)) ?
4 : 1;
2507 $right_column_html .= "\n" . '<td class="print_ignore" ' . $colspan
2511 $this->__set('display_params', $display_params);
2513 return $right_column_html;
2515 } // end of the '_getColumnAtRightSide()' function
2519 * Prepares the display for a value
2521 * @param string $class class of table cell
2522 * @param bool $condition_field whether to add CSS class condition
2523 * @param string $value value to display
2525 * @return string the td
2529 * @see _getDataCellForGeometryColumns(),
2530 * _getDataCellForNonNumericColumns()
2532 private function _buildValueDisplay($class, $condition_field, $value)
2534 return '<td class="left ' . $class . ($condition_field ?
' condition' : '')
2535 . '">' . $value . '</td>';
2536 } // end of the '_buildValueDisplay()' function
2540 * Prepares the display for a null value
2542 * @param string $class class of table cell
2543 * @param bool $condition_field whether to add CSS class condition
2544 * @param object $meta the meta-information about this field
2545 * @param string $align cell alignment
2547 * @return string the td
2551 * @see _getDataCellForNumericColumns(),
2552 * _getDataCellForGeometryColumns(),
2553 * _getDataCellForNonNumericColumns()
2555 private function _buildNullDisplay($class, $condition_field, $meta, $align = '')
2557 // the null class is needed for grid editing
2558 $decimals = isset($meta->decimals
) ?
$meta->decimals
: '-1';
2559 return '<td ' . $align . ' data-decimals="' . $decimals
2560 . '" data-type="' . $meta->type
. '" class="'
2562 $class, $condition_field, $meta, ''
2564 . ' null"><i>NULL</i></td>';
2565 } // end of the '_buildNullDisplay()' function
2569 * Prepares the display for an empty value
2571 * @param string $class class of table cell
2572 * @param bool $condition_field whether to add CSS class condition
2573 * @param object $meta the meta-information about this field
2574 * @param string $align cell alignment
2576 * @return string the td
2580 * @see _getDataCellForNumericColumns(),
2581 * _getDataCellForGeometryColumns(),
2582 * _getDataCellForNonNumericColumns()
2584 private function _buildEmptyDisplay($class, $condition_field, $meta, $align = '')
2586 return '<td ' . $align . ' class="'
2588 $class, $condition_field, $meta, 'nowrap'
2591 } // end of the '_buildEmptyDisplay()' function
2595 * Adds the relevant classes.
2597 * @param string $class class of table cell
2598 * @param bool $condition_field whether to add CSS class
2600 * @param object $meta the meta-information about the
2602 * @param string $nowrap avoid wrapping
2603 * @param bool $is_field_truncated is field truncated (display ...)
2604 * @param object|string $transformation_plugin transformation plugin.
2605 * Can also be the default function:
2606 * PMA_mimeDefaultFunction
2607 * @param string $default_function default transformation function
2609 * @return string the list of classes
2613 * @see _buildNullDisplay(), _getRowData()
2615 private function _addClass(
2616 $class, $condition_field, $meta, $nowrap, $is_field_truncated = false,
2617 $transformation_plugin = '', $default_function = ''
2624 if (isset($meta->mimetype
)) {
2625 $classes[] = preg_replace('/\//', '_', $meta->mimetype
);
2628 if ($condition_field) {
2629 $classes[] = 'condition';
2632 if ($is_field_truncated) {
2633 $classes[] = 'truncated';
2636 $mime_map = $this->__get('mime_map');
2637 $orgFullColName = $this->__get('db') . '.' . $meta->orgtable
2638 . '.' . $meta->orgname
;
2639 if ($transformation_plugin != $default_function
2640 ||
!empty($mime_map[$orgFullColName]['input_transformation'])
2642 $classes[] = 'transformed';
2645 // Define classes to be added to this data field based on the type of data
2652 foreach ($matches as $key => $value) {
2653 if (mb_strpos($meta->flags
, $key) !== false) {
2654 $classes[] = $value;
2658 if (mb_strpos($meta->type
, 'bit') !== false) {
2662 return implode(' ', $classes);
2663 } // end of the '_addClass()' function
2666 * Prepare the body of the results table
2668 * @param integer &$dt_result the link id associated to the query
2669 * which results have to be displayed which
2670 * results have to be displayed
2671 * @param array &$displayParts which elements to display
2672 * @param array $map the list of relations
2673 * @param array $analyzed_sql_results analyzed sql results
2674 * @param boolean $is_limited_display with limited operations or not
2676 * @return string $table_body_html html content
2678 * @global array $row current row data
2684 private function _getTableBody(
2685 &$dt_result, &$displayParts, $map, $analyzed_sql_results,
2686 $is_limited_display = false
2689 global $row; // mostly because of browser transformations,
2690 // to make the row-data accessible in a plugin
2692 $table_body_html = '';
2694 // query without conditions to shorten URLs when needed, 200 is just
2695 // guess, it should depend on remaining URL length
2696 $url_sql_query = $this->_getUrlSqlQuery($analyzed_sql_results);
2698 $display_params = $this->__get('display_params');
2700 if (! is_array($map)) {
2705 $display_params['edit'] = array();
2706 $display_params['copy'] = array();
2707 $display_params['delete'] = array();
2708 $display_params['data'] = array();
2709 $display_params['row_delete'] = array();
2710 $this->__set('display_params', $display_params);
2712 // name of the class added to all grid editable elements;
2713 // if we don't have all the columns of a unique key in the result set,
2714 // do not permit grid editing
2715 if ($is_limited_display ||
! $this->__get('editable')) {
2716 $grid_edit_class = '';
2718 switch ($GLOBALS['cfg']['GridEditing']) {
2719 case 'double-click':
2720 // trying to reduce generated HTML by using shorter
2721 // classes like click1 and click2
2722 $grid_edit_class = 'grid_edit click2';
2725 $grid_edit_class = 'grid_edit click1';
2727 default: // 'disabled'
2728 $grid_edit_class = '';
2733 // prepare to get the column order, if available
2734 list($col_order, $col_visib) = $this->_getColumnParams(
2735 $analyzed_sql_results
2738 // Correction University of Virginia 19991216 in the while below
2739 // Previous code assumed that all tables have keys, specifically that
2740 // the phpMyAdmin GUI should support row delete/edit only for such
2742 // Although always using keys is arguably the prescribed way of
2743 // defining a relational table, it is not required. This will in
2744 // particular be violated by the novice.
2745 // We want to encourage phpMyAdmin usage by such novices. So the code
2746 // below has been changed to conditionally work as before when the
2747 // table being displayed has one or more keys; but to display
2748 // delete/edit options correctly for tables without keys.
2750 $whereClauseMap = $this->__get('whereClauseMap');
2751 while ($row = $GLOBALS['dbi']->fetchRow($dt_result)) {
2753 // add repeating headers
2754 if ((($row_no != 0) && ($_SESSION['tmpval']['repeat_cells'] != 0))
2755 && !($row_no %
$_SESSION['tmpval']['repeat_cells'])
2757 $table_body_html .= $this->_getRepeatingHeaders(
2762 $tr_class = array();
2763 if ($GLOBALS['cfg']['BrowsePointerEnable'] != true) {
2764 $tr_class[] = 'nopointer';
2766 if ($GLOBALS['cfg']['BrowseMarkerEnable'] != true) {
2767 $tr_class[] = 'nomarker';
2770 // pointer code part
2771 $classes = (empty($tr_class) ?
' ' : 'class="' . implode(' ', $tr_class) . '"');
2772 $table_body_html .= '<tr ' . $classes . ' >';
2774 // 1. Prepares the row
2776 // In print view these variable needs to be initialized
2777 $del_url = $del_str = $edit_anchor_class
2778 = $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null;
2780 // 1.2 Defines the URLs for the modify/delete link(s)
2782 if (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
2783 ||
($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)
2786 // Results from a "SELECT" statement -> builds the
2787 // WHERE clause to use in links (a unique key if possible)
2789 * @todo $where_clause could be empty, for example a table
2790 * with only one field and it's a BLOB; in this case,
2791 * avoid to display the delete and edit links
2793 list($where_clause, $clause_is_unique, $condition_array)
2794 = Util
::getUniqueCondition(
2795 $dt_result, // handle
2796 $this->__get('fields_cnt'), // fields_cnt
2797 $this->__get('fields_meta'), // fields_meta
2799 false, // force_unique
2800 $this->__get('table'), // restrict_to_table
2801 $analyzed_sql_results // analyzed_sql_results
2803 $whereClauseMap[$row_no][$this->__get('table')] = $where_clause;
2804 $this->__set('whereClauseMap', $whereClauseMap);
2806 $where_clause_html = htmlspecialchars($where_clause);
2808 // 1.2.1 Modify link(s) - update row case
2809 if ($displayParts['edit_lnk'] == self
::UPDATE_ROW
) {
2811 list($edit_url, $copy_url, $edit_str, $copy_str,
2813 = $this->_getModifiedLinks(
2815 $clause_is_unique, $url_sql_query
2820 // 1.2.2 Delete/Kill link(s)
2821 list($del_url, $del_str, $js_conf)
2822 = $this->_getDeleteAndKillLinks(
2823 $where_clause, $clause_is_unique,
2824 $url_sql_query, $displayParts['del_lnk'],
2828 // 1.3 Displays the links at left if required
2829 if (($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_LEFT
)
2830 ||
($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_BOTH
)
2833 $table_body_html .= $this->_getPlacedLinks(
2834 self
::POSITION_LEFT
, $del_url, $displayParts, $row_no,
2835 $where_clause, $where_clause_html, $condition_array,
2836 $edit_url, $copy_url, $edit_anchor_class,
2837 $edit_str, $copy_str, $del_str, $js_conf
2840 } elseif ($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_NONE
) {
2842 $table_body_html .= $this->_getPlacedLinks(
2843 self
::POSITION_NONE
, $del_url, $displayParts, $row_no,
2844 $where_clause, $where_clause_html, $condition_array,
2845 $edit_url, $copy_url, $edit_anchor_class,
2846 $edit_str, $copy_str, $del_str, $js_conf
2852 // 2. Displays the rows' values
2853 if (is_null($this->__get('mime_map'))) {
2854 $this->_setMimeMap();
2856 $table_body_html .= $this->_getRowValues(
2865 $analyzed_sql_results
2868 // 3. Displays the modify/delete links on the right if required
2869 if (($displayParts['edit_lnk'] != self
::NO_EDIT_OR_DELETE
)
2870 ||
($displayParts['del_lnk'] != self
::NO_EDIT_OR_DELETE
)
2872 if (($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_RIGHT
)
2873 ||
($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_BOTH
)
2876 $table_body_html .= $this->_getPlacedLinks(
2877 self
::POSITION_RIGHT
, $del_url, $displayParts, $row_no,
2878 $where_clause, $where_clause_html, $condition_array,
2879 $edit_url, $copy_url, $edit_anchor_class,
2880 $edit_str, $copy_str, $del_str, $js_conf
2886 $table_body_html .= '</tr>';
2887 $table_body_html .= "\n";
2892 return $table_body_html;
2894 } // end of the '_getTableBody()' function
2897 * Sets the MIME details of the columns in the results set
2901 private function _setMimeMap()
2903 $fields_meta = $this->__get('fields_meta');
2907 for ($currentColumn = 0;
2908 $currentColumn < $this->__get('fields_cnt');
2911 $meta = $fields_meta[$currentColumn];
2912 $orgFullTableName = $this->__get('db') . '.' . $meta->orgtable
;
2914 if ($GLOBALS['cfgRelation']['commwork']
2915 && $GLOBALS['cfgRelation']['mimework']
2916 && $GLOBALS['cfg']['BrowseMIME']
2917 && ! $_SESSION['tmpval']['hide_transformation']
2918 && empty($added[$orgFullTableName])
2920 $mimeMap = array_merge(
2922 PMA_getMIME($this->__get('db'), $meta->orgtable
, false, true)
2924 $added[$orgFullTableName] = true;
2928 // special browser transformation for some SHOW statements
2929 if ($this->__get('is_show')
2930 && ! $_SESSION['tmpval']['hide_transformation']
2933 '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
2934 . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
2936 $this->__get('sql_query'), $which
2939 if (isset($which[1])) {
2940 $str = ' ' . strtoupper($which[1]);
2941 $isShowProcessList = strpos($str, 'PROCESSLIST') > 0;
2942 if ($isShowProcessList) {
2943 $mimeMap['..Info'] = array(
2944 'mimetype' => 'Text_Plain',
2945 'transformation' => 'output/Text_Plain_Sql.php',
2949 $isShowCreateTable = preg_match(
2950 '@CREATE[[:space:]]+TABLE@i', $this->__get('sql_query')
2952 if ($isShowCreateTable) {
2953 $mimeMap['..Create Table'] = array(
2954 'mimetype' => 'Text_Plain',
2955 'transformation' => 'output/Text_Plain_Sql.php',
2961 $this->__set('mime_map', $mimeMap);
2965 * Get the values for one data row
2967 * @param integer &$dt_result the link id associated to
2968 * the query which results
2969 * have to be displayed which
2970 * results have to be
2972 * @param array $row current row data
2973 * @param integer $row_no the index of current row
2974 * @param array $col_order the column order false when
2975 * a property not found false
2976 * when a property not found
2977 * @param array $map the list of relations
2978 * @param string $grid_edit_class the class for all editable
2980 * @param boolean|array|string $col_visib column is visible(false);
2981 * column isn't visible(string
2983 * @param string $url_sql_query the analyzed sql query
2984 * @param array $analyzed_sql_results analyzed sql results
2986 * @return string $row_values_html html content
2990 * @see _getTableBody()
2992 private function _getRowValues(
2993 &$dt_result, $row, $row_no, $col_order, $map,
2994 $grid_edit_class, $col_visib,
2995 $url_sql_query, $analyzed_sql_results
2997 $row_values_html = '';
2999 // Following variable are needed for use in isset/empty or
3000 // use with array indexes/safe use in foreach
3001 $sql_query = $this->__get('sql_query');
3002 $fields_meta = $this->__get('fields_meta');
3003 $highlight_columns = $this->__get('highlight_columns');
3004 $mime_map = $this->__get('mime_map');
3006 $row_info = $this->_getRowInfoForSpecialLinks($row, $col_order);
3008 $whereClauseMap = $this->__get('whereClauseMap');
3010 $columnCount = $this->__get('fields_cnt');
3011 for ($currentColumn = 0;
3012 $currentColumn < $columnCount;
3015 // assign $i with appropriate column order
3016 $i = $col_order ?
$col_order[$currentColumn] : $currentColumn;
3018 $meta = $fields_meta[$i];
3020 = $this->__get('db') . '.' . $meta->orgtable
. '.' . $meta->orgname
;
3022 $not_null_class = $meta->not_null ?
'not_null' : '';
3023 $relation_class = isset($map[$meta->name
]) ?
'relation' : '';
3024 $hide_class = ($col_visib && ! $col_visib[$currentColumn])
3027 $grid_edit = $meta->orgtable
!= '' ?
$grid_edit_class : '';
3029 // handle datetime-related class, for grid editing
3031 = $this->_getClassForDateTimeRelatedFields($meta->type
);
3033 $is_field_truncated = false;
3034 // combine all the classes applicable to this column's value
3035 $class = $this->_getClassesForColumn(
3036 $grid_edit, $not_null_class, $relation_class,
3037 $hide_class, $field_type_class
3040 // See if this column should get highlight because it's used in the
3042 $condition_field = (isset($highlight_columns)
3043 && (isset($highlight_columns[$meta->name
])
3044 ||
isset($highlight_columns[Util
::backquote($meta->name
)])))
3048 // Wrap MIME-transformations. [MIME]
3049 $default_function = 'PMA_mimeDefaultFunction'; // default_function
3050 $transformation_plugin = $default_function;
3051 $transform_options = array();
3053 if ($GLOBALS['cfgRelation']['mimework']
3054 && $GLOBALS['cfg']['BrowseMIME']
3057 if (isset($mime_map[$orgFullColName]['mimetype'])
3058 && !empty($mime_map[$orgFullColName]['transformation'])
3061 $file = $mime_map[$orgFullColName]['transformation'];
3062 $include_file = 'libraries/plugins/transformations/' . $file;
3064 if (file_exists($include_file)) {
3066 include_once $include_file;
3067 $class_name = PMA_getTransformationClassName($include_file);
3068 // todo add $plugin_manager
3069 $plugin_manager = null;
3070 $transformation_plugin = new $class_name(
3074 $transform_options = PMA_Transformation_getOptions(
3076 $mime_map[$orgFullColName]
3077 ['transformation_options']
3079 ?
$mime_map[$orgFullColName]
3080 ['transformation_options']
3084 $meta->mimetype
= str_replace(
3086 $mime_map[$orgFullColName]['mimetype']
3089 } // end if file_exists
3090 } // end if transformation is set
3091 } // end if mime/transformation works.
3093 // Check whether the field needs to display with syntax highlighting
3095 $dbLower = mb_strtolower($this->__get('db'));
3096 $tblLower = mb_strtolower($meta->orgtable
);
3097 $nameLower = mb_strtolower($meta->orgname
);
3098 if (! empty($this->transformation_info
[$dbLower][$tblLower][$nameLower])
3099 && (trim($row[$i]) != '')
3100 && ! $_SESSION['tmpval']['hide_transformation']
3102 include_once $this->transformation_info
3103 [$dbLower][$tblLower][$nameLower][0];
3104 $transformation_plugin = new $this->transformation_info
3105 [$dbLower][$tblLower][$nameLower][1](null);
3107 $transform_options = PMA_Transformation_getOptions(
3108 isset($mime_map[$orgFullColName]['transformation_options'])
3109 ?
$mime_map[$orgFullColName]['transformation_options']
3113 $meta->mimetype
= str_replace(
3115 $this->transformation_info
[$dbLower]
3116 [mb_strtolower($meta->orgtable
)]
3117 [mb_strtolower($meta->orgname
)][2]
3122 // Check for the predefined fields need to show as link in schemas
3123 include_once 'libraries/special_schema_links.lib.php';
3125 if (isset($GLOBALS['special_schema_links'])
3126 && (! empty($GLOBALS['special_schema_links'][$dbLower][$tblLower][$nameLower]))
3129 $linking_url = $this->_getSpecialLinkUrl(
3130 $row[$i], $row_info, mb_strtolower($meta->orgname
)
3132 $transformation_plugin = new Text_Plain_Link();
3134 $transform_options = array(
3139 $meta->mimetype
= str_replace(
3147 * The result set can have columns from more than one table,
3148 * this is why we have to check for the unique conditions
3149 * related to this table; however getUniqueCondition() is
3150 * costly and does not need to be called if we already know
3151 * the conditions for the current table.
3153 if (! isset($whereClauseMap[$row_no][$meta->orgtable
])) {
3154 $unique_conditions = Util
::getUniqueCondition(
3155 $dt_result, // handle
3156 $this->__get('fields_cnt'), // fields_cnt
3157 $this->__get('fields_meta'), // fields_meta
3159 false, // force_unique
3160 $meta->orgtable
, // restrict_to_table
3161 $analyzed_sql_results // analyzed_sql_results
3163 $whereClauseMap[$row_no][$meta->orgtable
] = $unique_conditions[0];
3166 $_url_params = array(
3167 'db' => $this->__get('db'),
3168 'table' => $meta->orgtable
,
3169 'where_clause' => $whereClauseMap[$row_no][$meta->orgtable
],
3170 'transform_key' => $meta->orgname
3173 if (! empty($sql_query)) {
3174 $_url_params['sql_query'] = $url_sql_query;
3177 $transform_options['wrapper_link'] = URL
::getCommon($_url_params);
3179 $display_params = $this->__get('display_params');
3181 // in some situations (issue 11406), numeric returns 1
3182 // even for a string type
3183 if ($meta->numeric == 1 && $meta->type
!= 'string') {
3186 $display_params['data'][$row_no][$i]
3187 = $this->_getDataCellForNumericColumns(
3193 $is_field_truncated,
3194 $analyzed_sql_results,
3195 $transformation_plugin,
3200 } elseif ($meta->type
== self
::GEOMETRY_FIELD
) {
3203 // Remove 'grid_edit' from $class as we do not allow to
3204 // inline-edit geometry data.
3205 $class = str_replace('grid_edit', '', $class);
3207 $display_params['data'][$row_no][$i]
3208 = $this->_getDataCellForGeometryColumns(
3215 $transformation_plugin,
3218 $analyzed_sql_results
3222 // n o t n u m e r i c
3224 $display_params['data'][$row_no][$i]
3225 = $this->_getDataCellForNonNumericColumns(
3232 $transformation_plugin,
3235 $is_field_truncated,
3236 $analyzed_sql_results,
3243 // output stored cell
3244 $row_values_html .= $display_params['data'][$row_no][$i];
3246 if (isset($display_params['rowdata'][$i][$row_no])) {
3247 $display_params['rowdata'][$i][$row_no]
3248 .= $display_params['data'][$row_no][$i];
3250 $display_params['rowdata'][$i][$row_no]
3251 = $display_params['data'][$row_no][$i];
3254 $this->__set('display_params', $display_params);
3258 return $row_values_html;
3260 } // end of the '_getRowValues()' function
3263 * Get link for display special schema links
3265 * @param string $column_value column value
3266 * @param array $row_info information about row
3267 * @param string $field_name column name
3269 * @return string generated link
3271 private function _getSpecialLinkUrl($column_value, $row_info, $field_name)
3274 $linking_url_params = array();
3275 $link_relations = $GLOBALS['special_schema_links']
3276 [mb_strtolower($this->__get('db'))]
3277 [mb_strtolower($this->__get('table'))]
3280 if (! is_array($link_relations['link_param'])) {
3281 $linking_url_params[$link_relations['link_param']] = $column_value;
3283 // Consider only the case of creating link for column field
3284 // sql query that needs to be passed as url param
3285 $sql = 'SELECT `' . $column_value . '` FROM `'
3286 . $row_info[$link_relations['link_param'][1]] . '`.`'
3287 . $row_info[$link_relations['link_param'][2]] . '`';
3288 $linking_url_params[$link_relations['link_param'][0]] = $sql;
3291 $divider = strpos($link_relations['default_page'], '?') ?
'&' : '?';
3292 if (empty($link_relations['link_dependancy_params'])) {
3293 return $link_relations['default_page']
3294 . URL
::getCommonRaw($linking_url_params, $divider);
3297 foreach ($link_relations['link_dependancy_params'] as $new_param) {
3299 // If param_info is an array, set the key and value
3301 if (is_array($new_param['param_info'])) {
3302 $linking_url_params[$new_param['param_info'][0]]
3303 = $new_param['param_info'][1];
3307 $linking_url_params[$new_param['param_info']]
3308 = $row_info[mb_strtolower($new_param['column_name'])];
3310 // Special case 1 - when executing routines, according
3311 // to the type of the routine, url param changes
3312 if (empty($row_info['routine_type'])) {
3317 return $link_relations['default_page']
3318 . URL
::getCommonRaw($linking_url_params, $divider);
3323 * Prepare row information for display special links
3325 * @param array $row current row data
3326 * @param array $col_order the column order
3328 * @return array $row_info associative array with column nama -> value
3330 private function _getRowInfoForSpecialLinks($row, $col_order)
3333 $row_info = array();
3334 $fields_meta = $this->__get('fields_meta');
3336 for ($n = 0; $n < $this->__get('fields_cnt'); ++
$n) {
3337 $m = $col_order ?
$col_order[$n] : $n;
3338 $row_info[mb_strtolower($fields_meta[$m]->name
)]
3347 * Get url sql query without conditions to shorten URLs
3349 * @param array $analyzed_sql_results analyzed sql results
3351 * @return string $url_sql analyzed sql query
3355 * @see _getTableBody()
3357 private function _getUrlSqlQuery($analyzed_sql_results)
3359 if (($analyzed_sql_results['querytype'] != 'SELECT')
3360 ||
(mb_strlen($this->__get('sql_query')) < 200)
3362 return $this->__get('sql_query');
3365 $query = 'SELECT ' . Query
::getClause(
3366 $analyzed_sql_results['statement'],
3367 $analyzed_sql_results['parser']->list,
3371 $from_clause = Query
::getClause(
3372 $analyzed_sql_results['statement'],
3373 $analyzed_sql_results['parser']->list,
3377 if (!empty($from_clause)) {
3378 $query .= ' FROM ' . $from_clause;
3383 } // end of the '_getUrlSqlQuery()' function
3387 * Get column order and column visibility
3389 * @param array $analyzed_sql_results analyzed sql results
3391 * @return array 2 element array - $col_order, $col_visib
3395 * @see _getTableBody()
3397 private function _getColumnParams($analyzed_sql_results)
3399 if ($this->_isSelect($analyzed_sql_results)) {
3400 $pmatable = new Table($this->__get('table'), $this->__get('db'));
3401 $col_order = $pmatable->getUiProp(Table
::PROP_COLUMN_ORDER
);
3402 $col_visib = $pmatable->getUiProp(Table
::PROP_COLUMN_VISIB
);
3408 return array($col_order, $col_visib);
3409 } // end of the '_getColumnParams()' function
3413 * Get HTML for repeating headers
3415 * @param array $display_params holds various display info
3417 * @return string $header_html html content
3421 * @see _getTableBody()
3423 private function _getRepeatingHeaders(
3426 $header_html = '<tr>' . "\n";
3428 if ($display_params['emptypre'] > 0) {
3430 $header_html .= ' <th colspan="'
3431 . $display_params['emptypre'] . '">'
3432 . "\n" . ' </th>' . "\n";
3434 } else if ($GLOBALS['cfg']['RowActionLinks'] == self
::POSITION_NONE
) {
3435 $header_html .= ' <th></th>' . "\n";
3438 foreach ($display_params['desc'] as $val) {
3439 $header_html .= $val;
3442 if ($display_params['emptyafter'] > 0) {
3444 .= ' <th colspan="' . $display_params['emptyafter']
3446 . "\n" . ' </th>' . "\n";
3448 $header_html .= '</tr>' . "\n";
3450 return $header_html;
3452 } // end of the '_getRepeatingHeaders()' function
3456 * Get modified links
3458 * @param string $where_clause the where clause of the sql
3459 * @param boolean $clause_is_unique the unique condition of clause
3460 * @param string $url_sql_query the analyzed sql query
3462 * @return array 5 element array - $edit_url, $copy_url,
3463 * $edit_str, $copy_str, $edit_anchor_class
3467 * @see _getTableBody()
3469 private function _getModifiedLinks(
3470 $where_clause, $clause_is_unique, $url_sql_query
3473 $_url_params = array(
3474 'db' => $this->__get('db'),
3475 'table' => $this->__get('table'),
3476 'where_clause' => $where_clause,
3477 'clause_is_unique' => $clause_is_unique,
3478 'sql_query' => $url_sql_query,
3479 'goto' => 'sql.php',
3482 $edit_url = 'tbl_change.php'
3484 $_url_params +
array('default_action' => 'update')
3487 $copy_url = 'tbl_change.php'
3489 $_url_params +
array('default_action' => 'insert')
3492 $edit_str = $this->_getActionLinkContent(
3493 'b_edit.png', __('Edit')
3495 $copy_str = $this->_getActionLinkContent(
3496 'b_insrow.png', __('Copy')
3499 // Class definitions required for grid editing jQuery scripts
3500 $edit_anchor_class = "edit_row_anchor";
3501 if ($clause_is_unique == 0) {
3502 $edit_anchor_class .= ' nonunique';
3505 return array($edit_url, $copy_url, $edit_str, $copy_str, $edit_anchor_class);
3507 } // end of the '_getModifiedLinks()' function
3511 * Get delete and kill links
3513 * @param string $where_clause the where clause of the sql
3514 * @param boolean $clause_is_unique the unique condition of clause
3515 * @param string $url_sql_query the analyzed sql query
3516 * @param string $del_lnk the delete link of current row
3517 * @param array $row the current row
3519 * @return array 3 element array
3520 * $del_url, $del_str, $js_conf
3524 * @see _getTableBody()
3526 private function _getDeleteAndKillLinks(
3527 $where_clause, $clause_is_unique, $url_sql_query, $del_lnk, $row
3530 $goto = $this->__get('goto');
3532 if ($del_lnk == self
::DELETE_ROW
) { // delete row case
3534 $_url_params = array(
3535 'db' => $this->__get('db'),
3536 'table' => $this->__get('table'),
3537 'sql_query' => $url_sql_query,
3538 'message_to_show' => __('The row has been deleted.'),
3539 'goto' => (empty($goto) ?
'tbl_sql.php' : $goto),
3542 $lnk_goto = 'sql.php' . URL
::getCommonRaw($_url_params);
3544 $del_query = 'DELETE FROM '
3545 . Util
::backquote($this->__get('table'))
3546 . ' WHERE ' . $where_clause .
3547 ($clause_is_unique ?
'' : ' LIMIT 1');
3549 $_url_params = array(
3550 'db' => $this->__get('db'),
3551 'table' => $this->__get('table'),
3552 'sql_query' => $del_query,
3553 'message_to_show' => __('The row has been deleted.'),
3554 'goto' => $lnk_goto,
3556 $del_url = 'sql.php' . URL
::getCommon($_url_params);
3558 $js_conf = 'DELETE FROM ' . Sanitize
::jsFormat($this->__get('table'))
3559 . ' WHERE ' . Sanitize
::jsFormat($where_clause, false)
3560 . ($clause_is_unique ?
'' : ' LIMIT 1');
3562 $del_str = $this->_getActionLinkContent('b_drop.png', __('Delete'));
3564 } elseif ($del_lnk == self
::KILL_PROCESS
) { // kill process case
3566 $_url_params = array(
3567 'db' => $this->__get('db'),
3568 'table' => $this->__get('table'),
3569 'sql_query' => $url_sql_query,
3570 'goto' => 'index.php',
3573 $lnk_goto = 'sql.php' . URL
::getCommonRaw($_url_params);
3575 $kill = $GLOBALS['dbi']->getKillQuery($row[0]);
3577 $_url_params = array(
3579 'sql_query' => $kill,
3580 'goto' => $lnk_goto,
3583 $del_url = 'sql.php' . URL
::getCommon($_url_params);
3585 $del_str = Util
::getIcon(
3586 'b_drop.png', __('Kill')
3589 $del_url = $del_str = $js_conf = null;
3592 return array($del_url, $del_str, $js_conf);
3594 } // end of the '_getDeleteAndKillLinks()' function
3598 * Get content inside the table row action links (Edit/Copy/Delete)
3600 * @param string $icon The name of the file to get
3601 * @param string $display_text The text displaying after the image icon
3607 * @see _getModifiedLinks(), _getDeleteAndKillLinks()
3609 private function _getActionLinkContent($icon, $display_text)
3614 if (isset($GLOBALS['cfg']['RowActionType'])
3615 && $GLOBALS['cfg']['RowActionType'] == self
::ACTION_LINK_CONTENT_ICONS
3618 $linkContent .= '<span class="nowrap">'
3620 $icon, $display_text
3624 } else if (isset($GLOBALS['cfg']['RowActionType'])
3625 && $GLOBALS['cfg']['RowActionType'] == self
::ACTION_LINK_CONTENT_TEXT
3628 $linkContent .= '<span class="nowrap">' . $display_text . '</span>';
3632 $linkContent .= Util
::getIcon(
3633 $icon, $display_text
3638 return $linkContent;
3644 * Prepare placed links
3646 * @param string $dir the direction of links should place
3647 * @param string $del_url the url for delete row
3648 * @param array $displayParts which elements to display
3649 * @param integer $row_no the index of current row
3650 * @param string $where_clause the where clause of the sql
3651 * @param string $where_clause_html the html encoded where clause
3652 * @param array $condition_array array of keys (primary, unique, condition)
3653 * @param string $edit_url the url for edit row
3654 * @param string $copy_url the url for copy row
3655 * @param string $edit_anchor_class the class for html element for edit
3656 * @param string $edit_str the label for edit row
3657 * @param string $copy_str the label for copy row
3658 * @param string $del_str the label for delete row
3659 * @param string $js_conf text for the JS confirmation
3661 * @return string html content
3665 * @see _getTableBody()
3667 private function _getPlacedLinks(
3668 $dir, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html,
3669 $condition_array, $edit_url, $copy_url,
3670 $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf
3673 if (! isset($js_conf)) {
3677 return $this->_getCheckboxAndLinks(
3678 $dir, $del_url, $displayParts,
3679 $row_no, $where_clause, $where_clause_html, $condition_array,
3680 $edit_url, $copy_url, $edit_anchor_class,
3681 $edit_str, $copy_str, $del_str, $js_conf
3684 } // end of the '_getPlacedLinks()' function
3688 * Get the combined classes for a column
3690 * @param string $grid_edit_class the class for all editable columns
3691 * @param string $not_null_class the class for not null columns
3692 * @param string $relation_class the class for relations in a column
3693 * @param string $hide_class the class for visibility of a column
3694 * @param string $field_type_class the class related to type of the field
3696 * @return string $class the combined classes
3700 * @see _getTableBody()
3702 private function _getClassesForColumn(
3703 $grid_edit_class, $not_null_class, $relation_class,
3704 $hide_class, $field_type_class
3706 $class = 'data ' . $grid_edit_class . ' ' . $not_null_class . ' '
3707 . $relation_class . ' ' . $hide_class . ' ' . $field_type_class;
3711 } // end of the '_getClassesForColumn()' function
3715 * Get class for datetime related fields
3717 * @param string $type the type of the column field
3719 * @return string $field_type_class the class for the column
3723 * @see _getTableBody()
3725 private function _getClassForDateTimeRelatedFields($type)
3727 if ((substr($type, 0, 9) == self
::TIMESTAMP_FIELD
)
3728 ||
($type == self
::DATETIME_FIELD
)
3730 $field_type_class = 'datetimefield';
3731 } elseif ($type == self
::DATE_FIELD
) {
3732 $field_type_class = 'datefield';
3733 } elseif ($type == self
::TIME_FIELD
) {
3734 $field_type_class = 'timefield';
3735 } elseif ($type == self
::STRING_FIELD
) {
3736 $field_type_class = 'text';
3738 $field_type_class = '';
3740 return $field_type_class;
3741 } // end of the '_getClassForDateTimeRelatedFields()' function
3745 * Prepare data cell for numeric type fields
3747 * @param string $column the column's value
3748 * @param string $class the html class for column
3749 * @param boolean $condition_field the column should highlighted
3751 * @param object $meta the meta-information about this
3753 * @param array $map the list of relations
3754 * @param boolean $is_field_truncated the condition for blob data
3756 * @param array $analyzed_sql_results the analyzed query
3757 * @param object|string $transformation_plugin the name of transformation plugin
3758 * @param string $default_function the default transformation
3760 * @param array $transform_options the transformation parameters
3762 * @return string $cell the prepared cell, html content
3766 * @see _getTableBody()
3768 private function _getDataCellForNumericColumns(
3769 $column, $class, $condition_field, $meta, $map, $is_field_truncated,
3770 $analyzed_sql_results, $transformation_plugin, $default_function,
3774 if (! isset($column) ||
is_null($column)) {
3776 $cell = $this->_buildNullDisplay(
3777 'right ' . $class, $condition_field, $meta, ''
3780 } elseif ($column != '') {
3782 $nowrap = ' nowrap';
3783 $where_comparison = ' = ' . $column;
3785 $cell = $this->_getRowData(
3786 'right ' . $class, $condition_field,
3787 $analyzed_sql_results, $meta, $map, $column,
3788 $transformation_plugin, $default_function, $nowrap,
3789 $where_comparison, $transform_options,
3790 $is_field_truncated, ''
3794 $cell = $this->_buildEmptyDisplay(
3795 'right ' . $class, $condition_field, $meta, ''
3801 } // end of the '_getDataCellForNumericColumns()' function
3805 * Get data cell for geometry type fields
3807 * @param string $column the relevant column in data row
3808 * @param string $class the html class for column
3809 * @param object $meta the meta-information about
3811 * @param array $map the list of relations
3812 * @param array $_url_params the parameters for generate url
3813 * @param boolean $condition_field the column should highlighted
3815 * @param object|string $transformation_plugin the name of transformation
3817 * @param string $default_function the default transformation
3819 * @param string $transform_options the transformation parameters
3820 * @param array $analyzed_sql_results the analyzed query
3822 * @return string $cell the prepared data cell, html content
3826 * @see _getTableBody()
3828 private function _getDataCellForGeometryColumns(
3829 $column, $class, $meta, $map, $_url_params, $condition_field,
3830 $transformation_plugin, $default_function, $transform_options,
3831 $analyzed_sql_results
3833 if (! isset($column) ||
is_null($column)) {
3834 $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
3838 if ($column == '') {
3839 $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
3843 // Display as [GEOMETRY - (size)]
3844 if ($_SESSION['tmpval']['geoOption'] == self
::GEOMETRY_DISP_GEOM
) {
3845 $geometry_text = $this->_handleNonPrintableContents(
3846 strtoupper(self
::GEOMETRY_FIELD
), $column, $transformation_plugin,
3847 $transform_options, $default_function, $meta, $_url_params
3850 $cell = $this->_buildValueDisplay(
3851 $class, $condition_field, $geometry_text
3856 if ($_SESSION['tmpval']['geoOption'] == self
::GEOMETRY_DISP_WKT
) {
3857 // Prepare in Well Known Text(WKT) format.
3858 $where_comparison = ' = ' . $column;
3860 // Convert to WKT format
3861 $wktval = Util
::asWKT($column);
3863 $is_field_truncated,
3866 ) = $this->_getPartialText($wktval);
3868 $cell = $this->_getRowData(
3869 $class, $condition_field, $analyzed_sql_results, $meta, $map,
3870 $wktval, $transformation_plugin, $default_function, '',
3871 $where_comparison, $transform_options,
3872 $is_field_truncated, ''
3877 // Prepare in Well Known Binary (WKB) format.
3879 if ($_SESSION['tmpval']['display_binary']) {
3880 $where_comparison = ' = ' . $column;
3882 $wkbval = substr(bin2hex($column), 8);
3884 $is_field_truncated,
3887 ) = $this->_getPartialText($wkbval);
3889 $cell = $this->_getRowData(
3890 $class, $condition_field,
3891 $analyzed_sql_results, $meta, $map, $wkbval,
3892 $transformation_plugin, $default_function, '',
3893 $where_comparison, $transform_options,
3894 $is_field_truncated, ''
3899 $wkbval = $this->_handleNonPrintableContents(
3900 self
::BINARY_FIELD
, $column, $transformation_plugin,
3901 $transform_options, $default_function, $meta,
3905 $cell = $this->_buildValueDisplay(
3906 $class, $condition_field, $wkbval
3911 } // end of the '_getDataCellForGeometryColumns()' function
3915 * Get data cell for non numeric type fields
3917 * @param string $column the relevant column in data row
3918 * @param string $class the html class for column
3919 * @param object $meta the meta-information about
3921 * @param array $map the list of relations
3922 * @param array $_url_params the parameters for generate
3924 * @param boolean $condition_field the column should highlighted
3926 * @param object|string $transformation_plugin the name of transformation
3928 * @param string $default_function the default transformation
3930 * @param string $transform_options the transformation parameters
3931 * @param boolean $is_field_truncated is data truncated due to
3933 * @param array $analyzed_sql_results the analyzed query
3934 * @param integer &$dt_result the link id associated to
3935 * the query which results
3936 * have to be displayed
3937 * @param integer $col_index the column index
3939 * @return string $cell the prepared data cell, html content
3943 * @see _getTableBody()
3945 private function _getDataCellForNonNumericColumns(
3946 $column, $class, $meta, $map, $_url_params, $condition_field,
3947 $transformation_plugin, $default_function, $transform_options,
3948 $is_field_truncated, $analyzed_sql_results, &$dt_result, $col_index
3950 $original_length = 0;
3952 $is_analyse = $this->__get('is_analyse');
3953 $field_flags = $GLOBALS['dbi']->fieldFlags($dt_result, $col_index);
3955 $bIsText = gettype($transformation_plugin) === 'object'
3956 && strpos($transformation_plugin->getMIMEtype(), 'Text')
3959 // disable inline grid editing
3960 // if binary fields are protected
3961 // or transformation plugin is of non text type
3963 if ((stristr($field_flags, self
::BINARY_FIELD
)
3964 && ($GLOBALS['cfg']['ProtectBinary'] === 'all'
3965 ||
($GLOBALS['cfg']['ProtectBinary'] === 'noblob'
3966 && !stristr($meta->type
, self
::BLOB_FIELD
))
3967 ||
($GLOBALS['cfg']['ProtectBinary'] === 'blob'
3968 && stristr($meta->type
, self
::BLOB_FIELD
))))
3971 $class = str_replace('grid_edit', '', $class);
3974 if (! isset($column) ||
is_null($column)) {
3975 $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
3979 if ($column == '') {
3980 $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
3984 // Cut all fields to $GLOBALS['cfg']['LimitChars']
3985 // (unless it's a link-type transformation or binary)
3986 if (!(gettype($transformation_plugin) === "object"
3987 && strpos($transformation_plugin->getName(), 'Link') !== false)
3988 && !stristr($field_flags, self
::BINARY_FIELD
)
3991 $is_field_truncated,
3994 ) = $this->_getPartialText($column);
3998 if (isset($meta->_type
) && $meta->_type
=== MYSQLI_TYPE_BIT
) {
4000 $column = Util
::printableBitValue(
4001 $column, $meta->length
4004 // some results of PROCEDURE ANALYSE() are reported as
4005 // being BINARY but they are quite readable,
4006 // so don't treat them as BINARY
4007 } elseif (stristr($field_flags, self
::BINARY_FIELD
)
4008 && !(isset($is_analyse) && $is_analyse)
4010 // we show the BINARY or BLOB message and field's size
4011 // (or maybe use a transformation)
4012 $binary_or_blob = self
::BLOB_FIELD
;
4013 if ($meta->type
=== self
::STRING_FIELD
) {
4014 $binary_or_blob = self
::BINARY_FIELD
;
4016 $column = $this->_handleNonPrintableContents(
4017 $binary_or_blob, $column, $transformation_plugin,
4018 $transform_options, $default_function,
4019 $meta, $_url_params, $is_field_truncated
4021 $class = $this->_addClass(
4022 $class, $condition_field, $meta, '',
4023 $is_field_truncated, $transformation_plugin, $default_function
4025 $result = strip_tags($column);
4026 // disable inline grid editing
4027 // if binary or blob data is not shown
4028 if (stristr($result, $binary_or_blob)) {
4029 $class = str_replace('grid_edit', '', $class);
4035 $cell = $this->_buildValueDisplay(
4036 $class, $condition_field, $column
4041 // transform functions may enable no-wrapping:
4042 $function_nowrap = 'applyTransformationNoWrap';
4044 $bool_nowrap = (($default_function != $transformation_plugin)
4045 && function_exists($transformation_plugin->$function_nowrap()))
4046 ?
$transformation_plugin->$function_nowrap($transform_options)
4049 // do not wrap if date field type
4050 $nowrap = (preg_match('@DATE|TIME@i', $meta->type
)
4051 ||
$bool_nowrap) ?
' nowrap' : '';
4053 $where_comparison = ' = \''
4054 . $GLOBALS['dbi']->escapeString($column)
4057 $cell = $this->_getRowData(
4058 $class, $condition_field,
4059 $analyzed_sql_results, $meta, $map, $column,
4060 $transformation_plugin, $default_function, $nowrap,
4061 $where_comparison, $transform_options,
4062 $is_field_truncated, $original_length
4067 } // end of the '_getDataCellForNonNumericColumns()' function
4070 * Checks the posted options for viewing query results
4071 * and sets appropriate values in the session.
4073 * @todo make maximum remembered queries configurable
4074 * @todo move/split into SQL class!?
4075 * @todo currently this is called twice unnecessary
4076 * @todo ignore LIMIT and ORDER in query!?
4084 public function setConfigParamsForDisplayTable()
4087 $sql_md5 = md5($this->__get('sql_query'));
4089 if (isset($_SESSION['tmpval']['query'][$sql_md5])) {
4090 $query = $_SESSION['tmpval']['query'][$sql_md5];
4093 $query['sql'] = $this->__get('sql_query');
4095 if (empty($query['repeat_cells'])) {
4096 $query['repeat_cells'] = $GLOBALS['cfg']['RepeatCells'];
4099 // as this is a form value, the type is always string so we cannot
4100 // use PMA_isValid($_REQUEST['session_max_rows'], 'integer')
4101 if (PMA_isValid($_REQUEST['session_max_rows'], 'numeric')) {
4102 $query['max_rows'] = (int)$_REQUEST['session_max_rows'];
4103 unset($_REQUEST['session_max_rows']);
4104 } elseif ($_REQUEST['session_max_rows'] == self
::ALL_ROWS
) {
4105 $query['max_rows'] = self
::ALL_ROWS
;
4106 unset($_REQUEST['session_max_rows']);
4107 } elseif (empty($query['max_rows'])) {
4108 $query['max_rows'] = intval($GLOBALS['cfg']['MaxRows']);
4111 if (PMA_isValid($_REQUEST['pos'], 'numeric')) {
4112 $query['pos'] = $_REQUEST['pos'];
4113 unset($_REQUEST['pos']);
4114 } elseif (empty($query['pos'])) {
4119 $_REQUEST['pftext'],
4121 self
::DISPLAY_PARTIAL_TEXT
, self
::DISPLAY_FULL_TEXT
4125 $query['pftext'] = $_REQUEST['pftext'];
4126 unset($_REQUEST['pftext']);
4127 } elseif (empty($query['pftext'])) {
4128 $query['pftext'] = self
::DISPLAY_PARTIAL_TEXT
;
4132 $_REQUEST['relational_display'],
4134 self
::RELATIONAL_KEY
, self
::RELATIONAL_DISPLAY_COLUMN
4138 $query['relational_display'] = $_REQUEST['relational_display'];
4139 unset($_REQUEST['relational_display']);
4140 } elseif (empty($query['relational_display'])) {
4141 // The current session value has priority over a
4142 // change via Settings; this change will be apparent
4143 // starting from the next session
4144 $query['relational_display'] = $GLOBALS['cfg']['RelationalDisplay'];
4148 $_REQUEST['geoOption'],
4150 self
::GEOMETRY_DISP_WKT
, self
::GEOMETRY_DISP_WKB
,
4151 self
::GEOMETRY_DISP_GEOM
4155 $query['geoOption'] = $_REQUEST['geoOption'];
4156 unset($_REQUEST['geoOption']);
4157 } elseif (empty($query['geoOption'])) {
4158 $query['geoOption'] = self
::GEOMETRY_DISP_GEOM
;
4161 if (isset($_REQUEST['display_binary'])) {
4162 $query['display_binary'] = true;
4163 unset($_REQUEST['display_binary']);
4164 } elseif (isset($_REQUEST['display_options_form'])) {
4165 // we know that the checkbox was unchecked
4166 unset($query['display_binary']);
4167 } elseif (isset($_REQUEST['full_text_button'])) {
4168 // do nothing to keep the value that is there in the session
4170 // selected by default because some operations like OPTIMIZE TABLE
4171 // and all queries involving functions return "binary" contents,
4172 // according to low-level field flags
4173 $query['display_binary'] = true;
4176 if (isset($_REQUEST['display_blob'])) {
4177 $query['display_blob'] = true;
4178 unset($_REQUEST['display_blob']);
4179 } elseif (isset($_REQUEST['display_options_form'])) {
4180 // we know that the checkbox was unchecked
4181 unset($query['display_blob']);
4184 if (isset($_REQUEST['hide_transformation'])) {
4185 $query['hide_transformation'] = true;
4186 unset($_REQUEST['hide_transformation']);
4187 } elseif (isset($_REQUEST['display_options_form'])) {
4188 // we know that the checkbox was unchecked
4189 unset($query['hide_transformation']);
4192 // move current query to the last position, to be removed last
4193 // so only least executed query will be removed if maximum remembered
4194 // queries limit is reached
4195 unset($_SESSION['tmpval']['query'][$sql_md5]);
4196 $_SESSION['tmpval']['query'][$sql_md5] = $query;
4198 // do not exceed a maximum number of queries to remember
4199 if (count($_SESSION['tmpval']['query']) > 10) {
4200 array_shift($_SESSION['tmpval']['query']);
4201 //echo 'deleting one element ...';
4204 // populate query configuration
4205 $_SESSION['tmpval']['pftext']
4207 $_SESSION['tmpval']['relational_display']
4208 = $query['relational_display'];
4209 $_SESSION['tmpval']['geoOption']
4210 = $query['geoOption'];
4211 $_SESSION['tmpval']['display_binary'] = isset(
4212 $query['display_binary']
4214 $_SESSION['tmpval']['display_blob'] = isset(
4215 $query['display_blob']
4217 $_SESSION['tmpval']['hide_transformation'] = isset(
4218 $query['hide_transformation']
4220 $_SESSION['tmpval']['pos']
4222 $_SESSION['tmpval']['max_rows']
4223 = $query['max_rows'];
4224 $_SESSION['tmpval']['repeat_cells']
4225 = $query['repeat_cells'];
4229 * Prepare a table of results returned by a SQL query.
4231 * @param integer &$dt_result the link id associated to the query
4232 * which results have to be displayed
4233 * @param array &$displayParts the parts to display
4234 * @param array $analyzed_sql_results analyzed sql results
4235 * @param boolean $is_limited_display With limited operations or not
4237 * @return string $table_html Generated HTML content for resulted table
4243 public function getTable(
4244 &$dt_result, &$displayParts, $analyzed_sql_results,
4245 $is_limited_display = false
4249 * The statement this table is built for.
4250 * @var \PhpMyAdmin\SqlParser\Statements\SelectStatement
4252 $statement = $analyzed_sql_results['statement'];
4255 // Following variable are needed for use in isset/empty or
4256 // use with array indexes/safe use in foreach
4257 $fields_meta = $this->__get('fields_meta');
4258 $showtable = $this->__get('showtable');
4259 $printview = $this->__get('printview');
4261 // why was this called here? (already called from sql.php)
4262 //$this->setConfigParamsForDisplayTable();
4265 * @todo move this to a central place
4266 * @todo for other future table types
4268 $is_innodb = (isset($showtable['Type'])
4269 && $showtable['Type'] == self
::TABLE_TYPE_INNO_DB
);
4272 && PMA_isJustBrowsing($analyzed_sql_results, true)
4274 // "j u s t b r o w s i n g"
4276 $after_count = Util
::showHint(
4278 __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc].')
4286 // 1. ----- Prepares the work -----
4288 // 1.1 Gets the information about which functionalities should be
4294 ) = $this->_setDisplayPartsAndTotal($displayParts);
4296 // 1.2 Defines offsets for the next and previous pages
4297 if ($displayParts['nav_bar'] == '1') {
4298 list($pos_next, $pos_prev) = $this->_getOffsets();
4301 // 1.3 Extract sorting expressions.
4302 // we need $sort_expression and $sort_expression_nodirection
4303 // even if there are many table references
4304 $sort_expression = array();
4305 $sort_expression_nodirection = array();
4306 $sort_direction = array();
4308 if (!empty($statement->order
)) {
4309 foreach ($statement->order
as $o) {
4310 $sort_expression[] = $o->expr
->expr
. ' ' . $o->type
;
4311 $sort_expression_nodirection[] = $o->expr
->expr
;
4312 $sort_direction[] = $o->type
;
4315 $sort_expression[] = '';
4316 $sort_expression_nodirection[] = '';
4317 $sort_direction[] = '';
4320 $number_of_columns = count($sort_expression_nodirection);
4322 // 1.4 Prepares display of first and last value of the sorted column
4323 $sorted_column_message = '';
4324 for ( $i = 0; $i < $number_of_columns; $i++
) {
4325 $sorted_column_message .= $this->_getSortedColumnMessage(
4326 $dt_result, $sort_expression_nodirection[$i]
4330 // 2. ----- Prepare to display the top of the page -----
4332 // 2.1 Prepares a messages with position information
4333 if (($displayParts['nav_bar'] == '1') && isset($pos_next)) {
4335 $message = $this->_setMessageInformation(
4336 $sorted_column_message,
4337 $analyzed_sql_results,
4344 $table_html .= Util
::getMessage(
4345 $message, $this->__get('sql_query'), 'success'
4348 } elseif ((!isset($printview) ||
($printview != '1')) && !$is_limited_display) {
4350 $table_html .= Util
::getMessage(
4351 __('Your SQL query has been executed successfully.'),
4352 $this->__get('sql_query'), 'success'
4356 // 2.3 Prepare the navigation bars
4357 if (strlen($this->__get('table')) === 0) {
4359 if ($analyzed_sql_results['querytype'] == 'SELECT') {
4360 // table does not always contain a real table name,
4361 // for example in MySQL 5.0.x, the query SHOW STATUS
4362 // returns STATUS as a table name
4363 $this->__set('table', $fields_meta[0]->table
);
4365 $this->__set('table', '');
4370 // can the result be sorted?
4371 if ($displayParts['sort_lnk'] == '1') {
4373 // At this point, $sort_expression is an array but we only verify
4374 // the first element in case we could find that the table is
4375 // sorted by one of the choices listed in the
4376 // "Sort by key" drop-down
4377 list($unsorted_sql_query, $sort_by_key_html)
4378 = $this->_getUnsortedSqlAndSortByKeyDropDown(
4379 $analyzed_sql_results, $sort_expression[0]
4383 $sort_by_key_html = $unsorted_sql_query = '';
4386 if (($displayParts['nav_bar'] == '1') && (empty($statement->limit
))) {
4387 $table_html .= $this->_getPlacedTableNavigations(
4388 $pos_next, $pos_prev, self
::PLACE_TOP_DIRECTION_DROPDOWN
,
4389 $is_innodb, $sort_by_key_html
4393 // 2b ----- Get field references from Database -----
4394 // (see the 'relation' configuration variable)
4400 if (!empty($statement->from
)) {
4401 foreach ($statement->from
as $field) {
4402 if (!empty($field->table
)) {
4403 $target[] = $field->table
;
4408 if (strlen($this->__get('table')) > 0) {
4409 // This method set the values for $map array
4410 $this->_setParamForLinkForeignKeyRelatedTables($map);
4412 // Coming from 'Distinct values' action of structure page
4413 // We manipulate relations mechanism to show a link to related rows.
4414 if ($this->__get('is_browse_distinct')) {
4415 $map[$fields_meta[1]->name
] = array(
4416 $this->__get('table'),
4417 $fields_meta[1]->name
,
4425 // 3. ----- Prepare the results table -----
4426 if ($is_limited_display) {
4427 $table_html .= "<br>";
4430 $table_html .= $this->_getTableHeaders(
4432 $analyzed_sql_results,
4433 $unsorted_sql_query,
4435 $sort_expression_nodirection,
4440 $table_html .= '<tbody>' . "\n";
4442 $table_html .= $this->_getTableBody(
4446 $analyzed_sql_results,
4450 $this->__set('display_params', null);
4452 $table_html .= '</tbody>' . "\n" . '</table>';
4454 // 4. ----- Prepares the link for multi-fields edit and delete
4456 if ($displayParts['del_lnk'] == self
::DELETE_ROW
4457 && $displayParts['del_lnk'] != self
::KILL_PROCESS
4460 $table_html .= $this->_getMultiRowOperationLinks(
4462 $analyzed_sql_results,
4463 $displayParts['del_lnk']
4468 // 5. ----- Get the navigation bar at the bottom if required -----
4469 if (($displayParts['nav_bar'] == '1') && empty($statement->limit
)) {
4470 $table_html .= $this->_getPlacedTableNavigations(
4471 $pos_next, $pos_prev, self
::PLACE_BOTTOM_DIRECTION_DROPDOWN
,
4472 $is_innodb, $sort_by_key_html
4474 } elseif (! isset($printview) ||
($printview != '1')) {
4475 $table_html .= "\n" . '<br /><br />' . "\n";
4478 // 6. ----- Prepare "Query results operations"
4479 if ((! isset($printview) ||
($printview != '1')) && ! $is_limited_display) {
4480 $table_html .= $this->_getResultsOperations(
4481 $displayParts, $analyzed_sql_results
4487 } // end of the 'getTable()' function
4491 * Get offsets for next page and previous page
4493 * @return array array with two elements - $pos_next, $pos_prev
4499 private function _getOffsets()
4502 if ($_SESSION['tmpval']['max_rows'] == self
::ALL_ROWS
) {
4507 $pos_next = $_SESSION['tmpval']['pos']
4508 +
$_SESSION['tmpval']['max_rows'];
4510 $pos_prev = $_SESSION['tmpval']['pos']
4511 - $_SESSION['tmpval']['max_rows'];
4513 if ($pos_prev < 0) {
4518 return array($pos_next, $pos_prev);
4520 } // end of the '_getOffsets()' function
4524 * Prepare sorted column message
4526 * @param integer &$dt_result the link id associated to the
4527 * query which results have to
4529 * @param string $sort_expression_nodirection sort expression without direction
4531 * @return string html content
4532 * null if not found sorted column
4538 private function _getSortedColumnMessage(
4539 &$dt_result, $sort_expression_nodirection
4542 $fields_meta = $this->__get('fields_meta'); // To use array indexes
4544 if (empty($sort_expression_nodirection)) {
4548 if (mb_strpos($sort_expression_nodirection, '.') === false) {
4549 $sort_table = $this->__get('table');
4550 $sort_column = $sort_expression_nodirection;
4552 list($sort_table, $sort_column)
4553 = explode('.', $sort_expression_nodirection);
4556 $sort_table = Util
::unQuote($sort_table);
4557 $sort_column = Util
::unQuote($sort_column);
4559 // find the sorted column index in row result
4560 // (this might be a multi-table query)
4561 $sorted_column_index = false;
4563 foreach ($fields_meta as $key => $meta) {
4564 if (($meta->table
== $sort_table) && ($meta->name
== $sort_column)) {
4565 $sorted_column_index = $key;
4570 if ($sorted_column_index === false) {
4574 // fetch first row of the result set
4575 $row = $GLOBALS['dbi']->fetchRow($dt_result);
4577 // initializing default arguments
4578 $default_function = 'PMA_mimeDefaultFunction';
4579 $transformation_plugin = $default_function;
4580 $transform_options = array();
4582 // check for non printable sorted row data
4583 $meta = $fields_meta[$sorted_column_index];
4585 if (stristr($meta->type
, self
::BLOB_FIELD
)
4586 ||
($meta->type
== self
::GEOMETRY_FIELD
)
4589 $column_for_first_row = $this->_handleNonPrintableContents(
4590 $meta->type
, $row[$sorted_column_index],
4591 $transformation_plugin, $transform_options,
4592 $default_function, $meta
4596 $column_for_first_row = $row[$sorted_column_index];
4599 $column_for_first_row = mb_strtoupper(
4601 $column_for_first_row, 0, $GLOBALS['cfg']['LimitChars']
4605 // fetch last row of the result set
4606 $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1);
4607 $row = $GLOBALS['dbi']->fetchRow($dt_result);
4609 // check for non printable sorted row data
4610 $meta = $fields_meta[$sorted_column_index];
4611 if (stristr($meta->type
, self
::BLOB_FIELD
)
4612 ||
($meta->type
== self
::GEOMETRY_FIELD
)
4615 $column_for_last_row = $this->_handleNonPrintableContents(
4616 $meta->type
, $row[$sorted_column_index],
4617 $transformation_plugin, $transform_options,
4618 $default_function, $meta
4622 $column_for_last_row = $row[$sorted_column_index];
4625 $column_for_last_row = mb_strtoupper(
4627 $column_for_last_row, 0, $GLOBALS['cfg']['LimitChars']
4631 // reset to first row for the loop in _getTableBody()
4632 $GLOBALS['dbi']->dataSeek($dt_result, 0);
4634 // we could also use here $sort_expression_nodirection
4635 return ' [' . htmlspecialchars($sort_column)
4636 . ': <strong>' . htmlspecialchars($column_for_first_row) . ' - '
4637 . htmlspecialchars($column_for_last_row) . '</strong>]';
4638 } // end of the '_getSortedColumnMessage()' function
4642 * Set the content that needs to be shown in message
4644 * @param string $sorted_column_message the message for sorted column
4645 * @param array $analyzed_sql_results the analyzed query
4646 * @param integer $total the total number of rows returned by
4647 * the SQL query without any
4648 * programmatically appended LIMIT clause
4649 * @param integer $pos_next the offset for next page
4650 * @param string $pre_count the string renders before row count
4651 * @param string $after_count the string renders after row count
4653 * @return Message $message an object of Message
4659 private function _setMessageInformation(
4660 $sorted_column_message, $analyzed_sql_results, $total,
4661 $pos_next, $pre_count, $after_count
4664 $unlim_num_rows = $this->__get('unlim_num_rows'); // To use in isset()
4666 if (!empty($analyzed_sql_results['statement']->limit
)) {
4668 $first_shown_rec = $analyzed_sql_results['statement']->limit
->offset
;
4669 $row_count = $analyzed_sql_results['statement']->limit
->rowCount
;
4671 if ($row_count < $total) {
4672 $last_shown_rec = $first_shown_rec +
$row_count - 1;
4674 $last_shown_rec = $first_shown_rec +
$total - 1;
4677 } elseif (($_SESSION['tmpval']['max_rows'] == self
::ALL_ROWS
)
4678 ||
($pos_next > $total)
4681 $first_shown_rec = $_SESSION['tmpval']['pos'];
4682 $last_shown_rec = $total - 1;
4686 $first_shown_rec = $_SESSION['tmpval']['pos'];
4687 $last_shown_rec = $pos_next - 1;
4691 $table = new Table($this->__get('table'), $this->__get('db'));
4692 if ($table->isView()
4693 && ($total == $GLOBALS['cfg']['MaxExactCountViews'])
4696 $message = Message
::notice(
4698 'This view has at least this number of rows. '
4699 . 'Please refer to %sdocumentation%s.'
4703 $message->addParam('[doc@cfg_MaxExactCount]');
4704 $message->addParam('[/doc]');
4705 $message_view_warning = Util
::showHint($message);
4708 $message_view_warning = false;
4711 $message = Message
::success(__('Showing rows %1s - %2s'));
4712 $message->addParam($first_shown_rec);
4714 if ($message_view_warning !== false) {
4715 $message->addParamHtml('... ' . $message_view_warning);
4717 $message->addParam($last_shown_rec);
4720 $message->addText('(');
4722 if ($message_view_warning === false) {
4724 if (isset($unlim_num_rows) && ($unlim_num_rows != $total)) {
4725 $message_total = Message
::notice(
4726 $pre_count . __('%1$d total, %2$d in query')
4728 $message_total->addParam($total);
4729 $message_total->addParam($unlim_num_rows);
4731 $message_total = Message
::notice($pre_count . __('%d total'));
4732 $message_total->addParam($total);
4735 if (!empty($after_count)) {
4736 $message_total->addHtml($after_count);
4738 $message->addMessage($message_total, '');
4740 $message->addText(', ', '');
4743 $message_qt = Message
::notice(__('Query took %01.4f seconds.') . ')');
4744 $message_qt->addParam($this->__get('querytime'));
4746 $message->addMessage($message_qt, '');
4747 if (! is_null($sorted_column_message)) {
4748 $message->addHtml($sorted_column_message, '');
4753 } // end of the '_setMessageInformation()' function
4757 * Set the value of $map array for linking foreign key related tables
4759 * @param array &$map the list of relations
4767 private function _setParamForLinkForeignKeyRelatedTables(&$map)
4770 // To be able to later display a link to the related table,
4771 // we verify both types of relations: either those that are
4772 // native foreign keys or those defined in the phpMyAdmin
4773 // configuration storage. If no PMA storage, we won't be able
4774 // to use the "column to display" notion (for example show
4775 // the name related to a numeric id).
4776 $exist_rel = PMA_getForeigners(
4777 $this->__get('db'), $this->__get('table'), '', self
::POSITION_BOTH
4780 if (! empty($exist_rel)) {
4782 foreach ($exist_rel as $master_field => $rel) {
4783 if ($master_field != 'foreign_keys_data') {
4784 $display_field = PMA_getDisplayField(
4785 $rel['foreign_db'], $rel['foreign_table']
4787 $map[$master_field] = array(
4788 $rel['foreign_table'],
4789 $rel['foreign_field'],
4794 foreach ($rel as $key => $one_key) {
4795 foreach ($one_key['index_list'] as $index => $one_field) {
4796 $display_field = PMA_getDisplayField(
4797 isset($one_key['ref_db_name'])
4798 ?
$one_key['ref_db_name']
4800 $one_key['ref_table_name']
4803 $map[$one_field] = array(
4804 $one_key['ref_table_name'],
4805 $one_key['ref_index_list'][$index],
4807 isset($one_key['ref_db_name'])
4808 ?
$one_key['ref_db_name']
4817 } // end of the '_setParamForLinkForeignKeyRelatedTables()' function
4821 * Prepare multi field edit/delete links
4823 * @param integer &$dt_result the link id associated to the query which
4824 * results have to be displayed
4825 * @param array $analyzed_sql_results analyzed sql results
4826 * @param string $del_link the display element - 'del_link'
4828 * @return string $links_html html content
4834 private function _getMultiRowOperationLinks(
4835 &$dt_result, $analyzed_sql_results, $del_link
4838 $links_html = '<div class="print_ignore" >';
4839 $url_query = $this->__get('url_query');
4840 $delete_text = ($del_link == self
::DELETE_ROW
) ?
__('Delete') : __('Kill');
4842 $links_html .= '<img class="selectallarrow" width="38" height="22"'
4843 . ' src="' . $this->__get('pma_theme_image') . 'arrow_'
4844 . $this->__get('text_dir') . '.png' . '"'
4845 . ' alt="' . __('With selected:') . '" />';
4847 $links_html .= '<input type="checkbox" '
4848 . 'id="resultsForm_' . $this->__get('unique_id') . '_checkall" '
4849 . 'class="checkall_box" title="' . __('Check all') . '" /> '
4850 . '<label for="resultsForm_' . $this->__get('unique_id') . '_checkall">'
4851 . __('Check all') . '</label> '
4852 . '<i style="margin-left: 2em">' . __('With selected:') . '</i>' . "\n";
4854 $links_html .= Util
::getButtonOrImage(
4855 'submit_mult', 'mult_submit',
4856 __('Edit'), 'b_edit.png', 'edit'
4859 $links_html .= Util
::getButtonOrImage(
4860 'submit_mult', 'mult_submit',
4861 __('Copy'), 'b_insrow.png', 'copy'
4864 $links_html .= Util
::getButtonOrImage(
4865 'submit_mult', 'mult_submit',
4866 $delete_text, 'b_drop.png', 'delete'
4869 if ($analyzed_sql_results['querytype'] == 'SELECT') {
4870 $links_html .= Util
::getButtonOrImage(
4871 'submit_mult', 'mult_submit',
4872 __('Export'), 'b_tblexport.png', 'export'
4876 $links_html .= "</div>\n";
4878 $links_html .= '<input type="hidden" name="sql_query"'
4879 . ' value="' . htmlspecialchars($this->__get('sql_query')) . '" />'
4882 if (! empty($url_query)) {
4883 $links_html .= '<input type="hidden" name="url_query"'
4884 . ' value="' . $url_query . '" />' . "\n";
4887 // fetch last row of the result set
4888 $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1);
4889 $row = $GLOBALS['dbi']->fetchRow($dt_result);
4891 // $clause_is_unique is needed by getTable() to generate the proper param
4892 // in the multi-edit and multi-delete form
4893 list($where_clause, $clause_is_unique, $condition_array)
4894 = Util
::getUniqueCondition(
4895 $dt_result, // handle
4896 $this->__get('fields_cnt'), // fields_cnt
4897 $this->__get('fields_meta'), // fields_meta
4899 false, // force_unique
4900 false, // restrict_to_table
4901 $analyzed_sql_results // analyzed_sql_results
4903 unset($where_clause, $condition_array);
4905 // reset to first row for the loop in _getTableBody()
4906 $GLOBALS['dbi']->dataSeek($dt_result, 0);
4908 $links_html .= '<input type="hidden" name="clause_is_unique"'
4909 . ' value="' . $clause_is_unique . '" />' . "\n";
4911 $links_html .= '</form>' . "\n";
4915 } // end of the '_getMultiRowOperationLinks()' function
4919 * Prepare table navigation bar at the top or bottom
4921 * @param integer $pos_next the offset for the "next" page
4922 * @param integer $pos_prev the offset for the "previous" page
4923 * @param string $place the place to show navigation
4924 * @param boolean $is_innodb whether its InnoDB or not
4925 * @param string $sort_by_key_html the sort by key dialog
4927 * @return string html content of navigation bar
4933 private function _getPlacedTableNavigations(
4934 $pos_next, $pos_prev, $place, $is_innodb, $sort_by_key_html
4937 $navigation_html = '';
4939 if ($place == self
::PLACE_BOTTOM_DIRECTION_DROPDOWN
) {
4940 $navigation_html .= '<br />' . "\n";
4943 $navigation_html .= $this->_getTableNavigation(
4944 $pos_next, $pos_prev, $is_innodb, $sort_by_key_html
4947 if ($place == self
::PLACE_TOP_DIRECTION_DROPDOWN
) {
4948 $navigation_html .= "\n";
4951 return $navigation_html;
4953 } // end of the '_getPlacedTableNavigations()' function
4956 * Generates HTML to display the Create view in span tag
4958 * @param array $analyzed_sql_results analyzed sql results
4959 * @param string $url_query String with URL Parameters
4965 * @see _getResultsOperations()
4967 private function _getLinkForCreateView($analyzed_sql_results, $url_query)
4969 $results_operations_html = '';
4970 if (empty($analyzed_sql_results['procedure'])) {
4972 $ajax_class = ' ajax';
4974 $results_operations_html .= '<span>'
4975 . Util
::linkOrButton(
4976 'view_create.php' . $url_query,
4978 'b_view_add.png', __('Create view'), true
4980 array('class' => 'create_view' . $ajax_class), true, true, ''
4984 return $results_operations_html;
4989 * Calls the _getResultsOperations with $only_view as true
4991 * @param array $analyzed_sql_results analyzed sql results
4998 public function getCreateViewQueryResultOp($analyzed_sql_results)
5001 $results_operations_html = '';
5002 //calling to _getResultOperations with a fake $displayParts
5003 //and setting only_view parameter to be true to generate just view
5004 $results_operations_html .= $this->_getResultsOperations(
5006 $analyzed_sql_results,
5009 return $results_operations_html;
5013 * Get copy to clipboard links for results operations
5015 * @return string $html
5019 private function _getCopytoclipboardLinks()
5021 $html = Util
::linkOrButton(
5024 'b_insrow.png', __('Copy to clipboard'), true
5026 array('id' => 'copyToClipBoard'),
5029 'copy_to_clip_board'
5036 * Get printview links for results operations
5038 * @return string $html
5042 private function _getPrintviewLinks()
5044 $html = Util
::linkOrButton(
5047 'b_print.png', __('Print'), true
5049 array('id' => 'printView'),
5059 * Get operations that are available on results.
5061 * @param array $displayParts the parts to display
5062 * @param array $analyzed_sql_results analyzed sql results
5063 * @param boolean $only_view Whether to show only view
5065 * @return string $results_operations_html html content
5071 private function _getResultsOperations(
5072 $displayParts, $analyzed_sql_results, $only_view = false
5076 $results_operations_html = '';
5077 $fields_meta = $this->__get('fields_meta'); // To safe use in foreach
5078 $header_shown = false;
5079 $header = '<fieldset class="print_ignore" ><legend>'
5080 . __('Query results operations') . '</legend>';
5082 $_url_params = array(
5083 'db' => $this->__get('db'),
5084 'table' => $this->__get('table'),
5086 'sql_query' => $this->__get('sql_query'),
5088 $url_query = URL
::getCommon($_url_params);
5090 if (!$header_shown) {
5091 $results_operations_html .= $header;
5092 $header_shown = true;
5094 // if empty result set was produced we need to
5095 // show only view and not other options
5097 $results_operations_html .= $this->_getLinkForCreateView(
5098 $analyzed_sql_results, $url_query
5101 if ($header_shown) {
5102 $results_operations_html .= '</fieldset><br />';
5104 return $results_operations_html;
5107 // Displays "printable view" link if required
5108 if ($displayParts['pview_lnk'] == '1') {
5109 $results_operations_html .= $this->_getPrintviewLinks();
5110 $results_operations_html .= $this->_getCopytoclipboardLinks();
5111 } // end displays "printable view"
5114 // (the url_query has extra parameters that won't be used to export)
5115 // (the single_table parameter is used in PMA_getExportDisplay()
5116 // to hide the SQL and the structure export dialogs)
5117 // If the parser found a PROCEDURE clause
5118 // (most probably PROCEDURE ANALYSE()) it makes no sense to
5119 // display the Export link).
5120 if (($analyzed_sql_results['querytype'] == self
::QUERY_TYPE_SELECT
)
5121 && ! isset($printview)
5122 && empty($analyzed_sql_results['procedure'])
5125 if (count($analyzed_sql_results['select_tables']) == 1) {
5126 $_url_params['single_table'] = 'true';
5129 if (! $header_shown) {
5130 $results_operations_html .= $header;
5131 $header_shown = true;
5134 $_url_params['unlim_num_rows'] = $this->__get('unlim_num_rows');
5137 * At this point we don't know the table name; this can happen
5138 * for example with a query like
5139 * SELECT bike_code FROM (SELECT bike_code FROM bikes) tmp
5140 * As a workaround we set in the table parameter the name of the
5141 * first table of this database, so that tbl_export.php and
5142 * the script it calls do not fail
5144 if (empty($_url_params['table']) && ! empty($_url_params['db'])) {
5145 $_url_params['table'] = $GLOBALS['dbi']->fetchValue("SHOW TABLES");
5146 /* No result (probably no database selected) */
5147 if ($_url_params['table'] === false) {
5148 unset($_url_params['table']);
5152 $results_operations_html .= Util
::linkOrButton(
5153 'tbl_export.php' . URL
::getCommon($_url_params),
5155 'b_tblexport.png', __('Export'), true
5165 $results_operations_html .= Util
::linkOrButton(
5166 'tbl_chart.php' . URL
::getCommon($_url_params),
5168 'b_chart.png', __('Display chart'), true
5177 // prepare GIS chart
5178 $geometry_found = false;
5179 // If at least one geometry field is found
5180 foreach ($fields_meta as $meta) {
5181 if ($meta->type
== self
::GEOMETRY_FIELD
) {
5182 $geometry_found = true;
5187 if ($geometry_found) {
5188 $results_operations_html
5189 .= Util
::linkOrButton(
5190 'tbl_gis_visualization.php'
5191 . URL
::getCommon($_url_params),
5193 'b_globe.gif', __('Visualize GIS data'), true
5207 * @todo detect privileges to create a view
5208 * (but see 2006-01-19 note in display_create_table.lib.php,
5209 * I think we cannot detect db-specific privileges reliably)
5210 * Note: we don't display a Create view link if we found a PROCEDURE clause
5212 if (!$header_shown) {
5213 $results_operations_html .= $header;
5214 $header_shown = true;
5217 $results_operations_html .= $this->_getLinkForCreateView(
5218 $analyzed_sql_results, $url_query
5221 if ($header_shown) {
5222 $results_operations_html .= '</fieldset><br />';
5225 return $results_operations_html;
5227 } // end of the '_getResultsOperations()' function
5231 * Verifies what to do with non-printable contents (binary or BLOB)
5234 * @param string $category BLOB|BINARY|GEOMETRY
5235 * @param string $content the binary content
5236 * @param mixed $transformation_plugin transformation plugin.
5237 * Can also be the default function:
5238 * PMA_mimeDefaultFunction
5239 * @param string $transform_options transformation parameters
5240 * @param string $default_function default transformation function
5241 * @param object $meta the meta-information about the field
5242 * @param array $url_params parameters that should go to the
5244 * @param boolean &$is_truncated the result is truncated or not
5246 * @return mixed string or float
5250 * @see _getDataCellForGeometryColumns(),
5251 * _getDataCellForNonNumericColumns(),
5252 * _getSortedColumnMessage()
5254 private function _handleNonPrintableContents(
5255 $category, $content, $transformation_plugin, $transform_options,
5256 $default_function, $meta, $url_params = array(), &$is_truncated = null
5259 $is_truncated = false;
5260 $result = '[' . $category;
5262 if (isset($content)) {
5264 $size = strlen($content);
5265 $display_size = Util
::formatByteDown($size, 3, 1);
5266 $result .= ' - ' . $display_size[0] . ' ' . $display_size[1];
5270 $result .= ' - NULL';
5277 // if we want to use a text transformation on a BLOB column
5278 if (gettype($transformation_plugin) === "object") {
5279 $posMimeOctetstream = strpos(
5280 $transformation_plugin->getMIMESubtype(),
5283 $posMimeText = strpos($transformation_plugin->getMIMEtype(), 'Text');
5284 if ($posMimeOctetstream
5285 ||
$posMimeText !== false
5287 // Applying Transformations on hex string of binary data
5288 // seems more appropriate
5289 $result = pack("H*", bin2hex($content));
5297 if ($default_function != $transformation_plugin) {
5298 $result = $transformation_plugin->applyTransformation(
5306 $result = $default_function($result, array(), $meta);
5307 if (($_SESSION['tmpval']['display_binary']
5308 && $meta->type
=== self
::STRING_FIELD
)
5309 ||
($_SESSION['tmpval']['display_blob']
5310 && stristr($meta->type
, self
::BLOB_FIELD
))
5312 // in this case, restart from the original $content
5313 if (mb_check_encoding($content, 'utf-8')
5314 && !preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', $content)
5316 // show as text if it's valid utf-8
5317 $result = htmlspecialchars($content);
5319 $result = '0x' . bin2hex($content);
5325 ) = $this->_getPartialText($result);
5328 /* Create link to download */
5330 // in PHP < 5.5, empty() only checks variables
5331 $tmpdb = $this->__get('db');
5332 if (count($url_params) > 0
5333 && (!empty($tmpdb) && !empty($meta->orgtable
))
5335 $result = '<a href="tbl_get_field.php'
5336 . URL
::getCommon($url_params)
5337 . '" class="disableAjax">'
5343 } // end of the '_handleNonPrintableContents()' function
5347 * Retrieves the associated foreign key info for a data cell
5349 * @param array $map the list of relations
5350 * @param object $meta the meta-information about the field
5351 * @param string $where_comparison data for the where clause
5353 * @return string formatted data
5358 private function _getFromForeign($map, $meta, $where_comparison)
5360 $dispsql = 'SELECT '
5361 . Util
::backquote($map[$meta->name
][2])
5363 . Util
::backquote($map[$meta->name
][3])
5365 . Util
::backquote($map[$meta->name
][0])
5367 . Util
::backquote($map[$meta->name
][1])
5368 . $where_comparison;
5370 $dispresult = $GLOBALS['dbi']->tryQuery(
5373 DatabaseInterface
::QUERY_STORE
5376 if ($dispresult && $GLOBALS['dbi']->numRows($dispresult) > 0) {
5377 list($dispval) = $GLOBALS['dbi']->fetchRow($dispresult, 0);
5379 $dispval = __('Link not found!');
5382 $GLOBALS['dbi']->freeResult($dispresult);
5388 * Prepares the displayable content of a data cell in Browse mode,
5389 * taking into account foreign key description field and transformations
5391 * @param string $class css classes for the td element
5392 * @param bool $condition_field whether the column is a part of
5394 * @param array $analyzed_sql_results the analyzed query
5395 * @param object $meta the meta-information about the
5397 * @param array $map the list of relations
5398 * @param string $data data
5399 * @param object|string $transformation_plugin transformation plugin.
5400 * Can also be the default function:
5401 * PMA_mimeDefaultFunction
5402 * @param string $default_function default function
5403 * @param string $nowrap 'nowrap' if the content should
5405 * @param string $where_comparison data for the where clause
5406 * @param array $transform_options options for transformation
5407 * @param bool $is_field_truncated whether the field is truncated
5408 * @param string $original_length of a truncated column, or ''
5410 * @return string formatted data
5414 * @see _getDataCellForNumericColumns(), _getDataCellForGeometryColumns(),
5415 * _getDataCellForNonNumericColumns(),
5418 private function _getRowData(
5419 $class, $condition_field, $analyzed_sql_results, $meta, $map, $data,
5420 $transformation_plugin, $default_function, $nowrap, $where_comparison,
5421 $transform_options, $is_field_truncated, $original_length=''
5423 $relational_display = $_SESSION['tmpval']['relational_display'];
5424 $printview = $this->__get('printview');
5425 $decimals = isset($meta->decimals
) ?
$meta->decimals
: '-1';
5426 $result = '<td data-decimals="' . $decimals . '"'
5427 . ' data-type="' . $meta->type
. '"';
5429 if (! empty($original_length)) {
5430 // cannot use data-original-length
5431 $result .= ' data-originallength="' . $original_length . '"';
5434 $result .= ' class="'
5436 $class, $condition_field, $meta, $nowrap,
5437 $is_field_truncated, $transformation_plugin, $default_function
5441 if (!empty($analyzed_sql_results['statement']->expr
)) {
5442 foreach ($analyzed_sql_results['statement']->expr
as $expr) {
5443 if ((empty($expr->alias
)) ||
(empty($expr->column
))) {
5446 if (strcasecmp($meta->name
, $expr->alias
) == 0) {
5447 $meta->name
= $expr->column
;
5452 if (isset($map[$meta->name
])) {
5454 // Field to display from the foreign table?
5455 if (isset($map[$meta->name
][2])
5456 && strlen($map[$meta->name
][2]) > 0
5458 $dispval = $this->_getFromForeign(
5459 $map, $meta, $where_comparison
5463 } // end if... else...
5465 if (isset($printview) && ($printview == '1')) {
5467 $result .= ($transformation_plugin != $default_function
5468 ?
$transformation_plugin->applyTransformation(
5473 : $default_function($data)
5475 . ' <code>[->' . $dispval . ']</code>';
5479 if ($relational_display == self
::RELATIONAL_KEY
) {
5481 // user chose "relational key" in the display options, so
5482 // the title contains the display field
5483 $title = (! empty($dispval))
5484 ?
' title="' . htmlspecialchars($dispval) . '"'
5488 $title = ' title="' . htmlspecialchars($data) . '"';
5491 $_url_params = array(
5492 'db' => $map[$meta->name
][3],
5493 'table' => $map[$meta->name
][0],
5495 'sql_query' => 'SELECT * FROM '
5496 . Util
::backquote($map[$meta->name
][3]) . '.'
5497 . Util
::backquote($map[$meta->name
][0])
5499 . Util
::backquote($map[$meta->name
][1])
5500 . $where_comparison,
5503 $result .= '<a class="ajax" href="sql.php'
5504 . URL
::getCommon($_url_params)
5505 . '"' . $title . '>';
5507 if ($transformation_plugin != $default_function) {
5508 // always apply a transformation on the real data,
5509 // not on the display field
5510 $result .= $transformation_plugin->applyTransformation(
5517 if ($relational_display == self
::RELATIONAL_DISPLAY_COLUMN
5518 && ! empty($map[$meta->name
][2])
5520 // user chose "relational display field" in the
5521 // display options, so show display field in the cell
5522 $result .= $default_function($dispval);
5524 // otherwise display data in the cell
5525 $result .= $default_function($data);
5533 $result .= ($transformation_plugin != $default_function
5534 ?
$transformation_plugin->applyTransformation(
5539 : $default_function($data)
5543 $result .= '</td>' . "\n";
5547 } // end of the '_getRowData()' function
5551 * Prepares a checkbox for multi-row submits
5553 * @param string $del_url delete url
5554 * @param array $displayParts array with explicit indexes for all
5555 * the display elements
5556 * @param string $row_no the row number
5557 * @param string $where_clause_html url encoded where clause
5558 * @param array $condition_array array of conditions in the where clause
5559 * @param string $id_suffix suffix for the id
5560 * @param string $class css classes for the td element
5562 * @return string the generated HTML
5566 * @see _getTableBody(), _getCheckboxAndLinks()
5568 private function _getCheckboxForMultiRowSubmissions(
5569 $del_url, $displayParts, $row_no, $where_clause_html, $condition_array,
5575 if (! empty($del_url) && $displayParts['del_lnk'] != self
::KILL_PROCESS
) {
5578 if (! empty($class)) {
5579 $ret .= 'class="' . $class . '"';
5582 $ret .= ' class="center print_ignore">'
5583 . '<input type="checkbox" id="id_rows_to_delete'
5584 . $row_no . $id_suffix
5585 . '" name="rows_to_delete[' . $row_no . ']"'
5586 . ' class="multi_checkbox checkall"'
5587 . ' value="' . $where_clause_html . '" '
5589 . '<input type="hidden" class="condition_array" value="'
5590 . htmlspecialchars(json_encode($condition_array)) . '" />'
5596 } // end of the '_getCheckboxForMultiRowSubmissions()' function
5600 * Prepares an Edit link
5602 * @param string $edit_url edit url
5603 * @param string $class css classes for td element
5604 * @param string $edit_str text for the edit link
5605 * @param string $where_clause where clause
5606 * @param string $where_clause_html url encoded where clause
5608 * @return string the generated HTML
5612 * @see _getTableBody(), _getCheckboxAndLinks()
5614 private function _getEditLink(
5615 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5619 if (! empty($edit_url)) {
5621 $ret .= '<td class="' . $class . ' center print_ignore" '
5622 . ' ><span class="nowrap">'
5623 . Util
::linkOrButton(
5624 $edit_url, $edit_str, array(), false
5627 * Where clause for selecting this row uniquely is provided as
5628 * a hidden input. Used by jQuery scripts for handling grid editing
5630 if (! empty($where_clause)) {
5631 $ret .= '<input type="hidden" class="where_clause" value ="'
5632 . $where_clause_html . '" />';
5634 $ret .= '</span></td>';
5639 } // end of the '_getEditLink()' function
5643 * Prepares an Copy link
5645 * @param string $copy_url copy url
5646 * @param string $copy_str text for the copy link
5647 * @param string $where_clause where clause
5648 * @param string $where_clause_html url encoded where clause
5649 * @param string $class css classes for the td element
5651 * @return string the generated HTML
5655 * @see _getTableBody(), _getCheckboxAndLinks()
5657 private function _getCopyLink(
5658 $copy_url, $copy_str, $where_clause, $where_clause_html, $class
5662 if (! empty($copy_url)) {
5664 $ret .= '<td class="';
5665 if (! empty($class)) {
5666 $ret .= $class . ' ';
5669 $ret .= 'center print_ignore" ' . ' ><span class="nowrap">'
5670 . Util
::linkOrButton(
5671 $copy_url, $copy_str, array(), false
5675 * Where clause for selecting this row uniquely is provided as
5676 * a hidden input. Used by jQuery scripts for handling grid editing
5678 if (! empty($where_clause)) {
5679 $ret .= '<input type="hidden" class="where_clause" value="'
5680 . $where_clause_html . '" />';
5682 $ret .= '</span></td>';
5687 } // end of the '_getCopyLink()' function
5691 * Prepares a Delete link
5693 * @param string $del_url delete url
5694 * @param string $del_str text for the delete link
5695 * @param string $js_conf text for the JS confirmation
5696 * @param string $class css classes for the td element
5698 * @return string the generated HTML
5702 * @see _getTableBody(), _getCheckboxAndLinks()
5704 private function _getDeleteLink($del_url, $del_str, $js_conf, $class)
5708 if (empty($del_url)) {
5712 $ret .= '<td class="';
5713 if (! empty($class)) {
5714 $ret .= $class . ' ';
5716 $ajax = Response
::getInstance()->isAjax() ?
' ajax' : '';
5717 $ret .= 'center print_ignore" ' . ' >'
5718 . Util
::linkOrButton(
5721 array('class' => 'delete_row requireConfirm' . $ajax),
5724 . '<div class="hide">' . $js_conf . '</div>'
5729 } // end of the '_getDeleteLink()' function
5733 * Prepare checkbox and links at some position (left or right)
5734 * (only called for horizontal mode)
5736 * @param string $position the position of the checkbox and links
5737 * @param string $del_url delete url
5738 * @param array $displayParts array with explicit indexes for all the
5740 * @param string $row_no row number
5741 * @param string $where_clause where clause
5742 * @param string $where_clause_html url encoded where clause
5743 * @param array $condition_array array of conditions in the where clause
5744 * @param string $edit_url edit url
5745 * @param string $copy_url copy url
5746 * @param string $class css classes for the td elements
5747 * @param string $edit_str text for the edit link
5748 * @param string $copy_str text for the copy link
5749 * @param string $del_str text for the delete link
5750 * @param string $js_conf text for the JS confirmation
5752 * @return string the generated HTML
5756 * @see _getPlacedLinks()
5758 private function _getCheckboxAndLinks(
5759 $position, $del_url, $displayParts, $row_no, $where_clause,
5760 $where_clause_html, $condition_array,
5761 $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf
5766 if ($position == self
::POSITION_LEFT
) {
5768 $ret .= $this->_getCheckboxForMultiRowSubmissions(
5769 $del_url, $displayParts, $row_no, $where_clause_html,
5770 $condition_array, '_left', ''
5773 $ret .= $this->_getEditLink(
5774 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5777 $ret .= $this->_getCopyLink(
5778 $copy_url, $copy_str, $where_clause, $where_clause_html, ''
5781 $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
5783 } elseif ($position == self
::POSITION_RIGHT
) {
5785 $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
5787 $ret .= $this->_getCopyLink(
5788 $copy_url, $copy_str, $where_clause, $where_clause_html, ''
5791 $ret .= $this->_getEditLink(
5792 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5795 $ret .= $this->_getCheckboxForMultiRowSubmissions(
5796 $del_url, $displayParts, $row_no, $where_clause_html,
5797 $condition_array, '_right', ''
5800 } else { // $position == self::POSITION_NONE
5802 $ret .= $this->_getCheckboxForMultiRowSubmissions(
5803 $del_url, $displayParts, $row_no, $where_clause_html,
5804 $condition_array, '_left', ''
5810 } // end of the '_getCheckboxAndLinks()' function
5813 * Truncates given string based on LimitChars configuration
5814 * and Session pftext variable
5815 * (string is truncated only if necessary)
5817 * @param string $str string to be truncated
5823 * @see _handleNonPrintableContents(), _getDataCellForGeometryColumns(),
5824 * _getDataCellForNonNumericColumns
5826 private function _getPartialText($str)
5828 $original_length = mb_strlen($str);
5829 if ($original_length > $GLOBALS['cfg']['LimitChars']
5830 && $_SESSION['tmpval']['pftext'] === self
::DISPLAY_PARTIAL_TEXT
5833 $str, 0, $GLOBALS['cfg']['LimitChars']
5840 return array($truncated, $str, $original_length);