Translated using Weblate (Slovenian)
[phpmyadmin.git] / libraries / DisplayResults.php
blob18035cd95006d01c7c96b8d7ca6e7691fbec2697
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Hold the PMA\libraries\DisplayResults class
6 * @package PhpMyAdmin
7 */
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';
17 /**
18 * Handle all the functionalities related to displaying results
19 * of sql queries, stored procedure, browsing sql processes or
20 * displaying binary log.
22 * @package PhpMyAdmin
24 class DisplayResults
27 // Define constants
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 */
85 'db' => null,
87 /** string Table name */
88 'table' => null,
90 /** string the URL to go back in case of errors */
91 'goto' => null,
93 /** string the SQL query */
94 'sql_query' => null,
96 /**
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,
105 /** boolean */
106 'is_count' => null,
108 /** integer */
109 'is_export' => null,
111 /** boolean */
112 'is_func' => null,
114 /** integer */
115 'is_analyse' => null,
117 /** integer the total number of rows returned by the SQL query */
118 'num_rows' => null,
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 */
124 'querytime' => null,
126 /** string path for theme images directory */
127 'pma_theme_image' => null,
129 /** string */
130 'text_dir' => null,
132 /** boolean */
133 'is_maint' => null,
135 /** boolean */
136 'is_explain' => null,
138 /** boolean */
139 'is_show' => null,
141 /** boolean */
142 'is_browse_distinct' => null,
144 /** array table definitions */
145 'showtable' => null,
147 /** string */
148 'printview' => null,
150 /** string URL query */
151 'url_query' => null,
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 */
160 'mime_map' => null,
162 /** boolean */
163 'editable' => null,
165 /** random unique ID to distinguish result set */
166 'unique_id' => null,
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
202 * @return void
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
220 * @access public
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
236 * @return void
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',
243 'Text_Plain'
245 $sql_highlighting_data = array(
246 'libraries/plugins/transformations/output/Text_Plain_Sql.php',
247 'PMA\libraries\plugins\transformations\output\Text_Plain_Sql',
248 'Text_Plain'
250 $blob_sql_highlighting_data = array(
251 'libraries/plugins/transformations/output/Text_Octetstream_Sql.php',
252 'PMA\libraries\plugins\transformations\output\Text_Octetstream_Sql',
253 'Text_Octetstream'
255 $link_data = array(
256 'libraries/plugins/transformations/Text_Plain_Link.php',
257 'PMA\libraries\plugins\transformations\Text_Plain_Link',
258 'Text_Plain'
260 $this->transformation_info = array(
261 'information_schema' => array(
262 'events' => array(
263 'event_definition' => $sql_highlighting_data
265 'processlist' => array(
266 'info' => $sql_highlighting_data
268 'routines' => array(
269 'routine_definition' => $sql_highlighting_data
271 'triggers' => array(
272 'action_statement' => $sql_highlighting_data
274 'views' => array(
275 'view_definition' => $sql_highlighting_data
278 'mysql' => array(
279 'event' => array(
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(
287 'url' => $link_data
289 'help_topic' => array(
290 'example' => $sql_highlighting_data,
291 'url' => $link_data
293 'proc' => array(
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
299 'slow_log' => array(
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
388 * @return void
390 * @see sql.php
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
429 * @access private
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
453 * @access private
456 private function _setDisplayPartsForShow($displayParts)
458 preg_match(
459 '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
460 . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
461 . ')@i',
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) {
473 // no edit link
474 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
475 // "kill process" type edit link
476 $displayParts['del_lnk'] = self::KILL_PROCESS;
477 } else {
478 // Default case -> no links
479 // no edit link
480 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
481 // no delete link
482 $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
484 // Other settings
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
501 * @access private
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';
517 } else {
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
532 * @access private
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');
542 $prev_table = '';
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?
553 if ($is_link
554 && $prev_table != ''
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') {
567 break;
569 } // end if
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;
576 } // end for
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)
602 * @access private
604 * @see getTable()
606 private function _setDisplayPartsAndTotal($displayParts)
608 $the_total = 0;
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);
630 } else {
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
652 // false
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';
666 } // end if (3)
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
679 * @return boolean
681 * @access private
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
712 * @access private
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 = '';
723 if ($back) {
724 if (Util::showIcons('TableNavigationLinksMode')) {
725 $caption_output .= $caption;
727 if (Util::showText('TableNavigationLinksMode')) {
728 $caption_output .= '&nbsp;' . $title;
730 } else {
731 if (Util::showText('TableNavigationLinksMode')) {
732 $caption_output .= $title;
734 if (Util::showIcons('TableNavigationLinksMode')) {
735 $caption_output .= '&nbsp;' . $caption;
738 $title_output = ' title="' . $title . '"';
740 return '<td>'
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')
751 . '" />'
752 . $input_for_real_end
753 . '<input type="submit" name="navig"'
754 . ' class="ajax" '
755 . 'value="' . $caption_output . '" ' . $title_output . $onclick . ' />'
756 . '</form>'
757 . '</td>';
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)
769 * @access private
772 private function _getHtmlPageSelector($table_navigation_html)
774 $pageNow = @floor(
775 $_SESSION['tmpval']['pos']
776 / $_SESSION['tmpval']['max_rows']
777 ) + 1;
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(
801 'pos',
802 $_SESSION['tmpval']['max_rows'],
803 $pageNow, $nbTotalPage, 200, 5, 5, 20, 10
806 $table_navigation_html .= '</form>'
807 . '</td>';
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
822 * @access private
824 * @see _getTable()
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'));
836 // Navigation bar
837 $table_navigation_html
838 .= '<table class="navigation nospacing nopadding print_ignore">'
839 . '<tr>'
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
852 } // end move back
854 $nbTotalPage = 1;
855 //page redirection
856 // (unless we are showing all records)
857 if ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS) {
858 list(
859 $table_navigation_html,
860 $nbTotalPage
861 ) = $this->_getHtmlPageSelector($table_navigation_html);
864 $showing_all = false;
865 if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
866 $showing_all = true;
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
884 } // end move toward
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>';
902 } // end show all
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>'
908 . '</div>'
909 . '</td>'
910 . '<td>'
911 . '<div class="restore_column hide">'
912 . '<input type="submit" value="' . __('Restore column order') . '" />'
913 . '<div class="navigation_separator">|</div>'
914 . '</div>'
915 . '</td>';
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('
926 . 'this, '
927 . '\'session_max_rows\', '
928 . '\''
929 . str_replace('\'', '\\\'', __('%d is not valid row number.'))
930 . '\', '
931 . '1)'
932 . ' &amp;&amp; '
933 . 'checkFormElementInRange('
934 . 'this, '
935 . '\'pos\', '
936 . '\''
937 . str_replace('\'', '\\\'', __('%d is not valid row number.'))
938 . '\', '
939 . '0'
940 . (($this->__get('unlim_num_rows') > 0)
941 ? ', ' . ($this->__get('unlim_num_rows') - 1)
942 : ''
944 . ')'
945 . ')'
946 . '">';
948 $table_navigation_html .= URL::getHiddenInputs(
949 $this->__get('db'), $this->__get('table')
952 $table_navigation_html .= $this->_getAdditionalFieldsForTableNavigation(
953 $html_sql_query
956 $table_navigation_html .= '</form>'
957 . '</td>'
958 . '<td class="navigation_separator"></td>'
959 . '<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') . '" />'
964 . '</td>';
966 $table_navigation_html .= '<td>' . $sort_by_key_html . '</td>';
968 $table_navigation_html .= '<td class="navigation_separator"></td>'
969 . '</tr>'
970 . '</table>';
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
985 * @access private
987 * @see _getTableNavigation()
989 private function _getMoveBackwardButtonsForTableNavigation(
990 $html_sql_query, $pos_prev
992 return $this->_getTableNavigationButton(
993 '&lt;&lt;', _pgettext('First page', 'Begin'), 0, $html_sql_query, true
995 . $this->_getTableNavigationButton(
996 '&lt;', _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
1010 * @access private
1012 * @see _getTableNavigation()
1014 private function _getShowAllCheckboxForTableNavigation(
1015 $showing_all, $html_sql_query
1017 return "\n"
1018 . '<td>'
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')
1031 . '" />'
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>'
1037 . '</form>'
1038 . '</td>';
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
1051 * @access private
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(
1061 '&gt;',
1062 _pgettext('Next page', 'Next'),
1063 $pos_next,
1064 $html_sql_query,
1065 false
1068 // prepare some options for the End button
1069 if ($is_innodb
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
1075 $onclick = '';
1076 } else {
1077 $input_for_real_end = $onclick = '';
1080 $maxRows = $_SESSION['tmpval']['max_rows'];
1081 $onsubmit = 'onsubmit="return '
1082 . (($_SESSION['tmpval']['pos']
1083 + $maxRows
1084 < $this->__get('unlim_num_rows')
1085 && $this->__get('num_rows') >= $maxRows)
1086 ? 'true'
1087 : 'false') . '"';
1089 // display the End button
1090 $buttons_html .= $this->_getTableNavigationButton(
1091 '&gt;&gt;',
1092 _pgettext('Last page', 'End'),
1093 @((ceil(
1094 $this->__get('unlim_num_rows')
1095 / $_SESSION['tmpval']['max_rows']
1096 )- 1) * $maxRows),
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
1107 * Number of rows
1109 * @param string $html_sql_query the sql encoded by htmlspecialchars()
1111 * @return string $additional_fields_html html content
1113 * @access private
1115 * @see _getTableNavigation()
1117 private function _getAdditionalFieldsForTableNavigation(
1118 $html_sql_query
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')
1126 . '" />'
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(
1139 '25' => 25,
1140 '50' => 50,
1141 '100' => 100,
1142 '250' => 250,
1143 '500' => 500
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
1164 * without direction
1165 * @param array $sort_direction sort direction
1166 * @param boolean $is_limited_display with limited operations
1167 * or not
1168 * @param string $unsorted_sql_query query without the sort part
1170 * @return string html content
1172 * @access private
1174 * @see getTableHeaders()
1176 private function _getTableHeadersForColumns(
1177 $displayParts, $analyzed_sql_results, $sort_expression,
1178 $sort_expression_nodirection, $sort_direction, $is_limited_display,
1179 $unsorted_sql_query
1181 $html = '';
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
1212 // where-query.
1213 $name = $fields_meta[$i]->name;
1214 $condition_field = (isset($highlight_columns[$name])
1215 || isset($highlight_columns[Util::backquote($name)]))
1216 ? true
1217 : false;
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,
1231 $col_visib[$j]
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";
1241 } else {
1242 // Results can't be sorted
1243 $html
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)
1253 . '">' . ' '
1254 . htmlspecialchars($fields_meta[$i]->name)
1255 . $comments . ' </th>';
1256 } // end else
1258 $this->__set('display_params', $display_params);
1260 } // end for
1261 return $html;
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
1272 * without direction
1273 * @param array $sort_direction sort direction
1274 * @param boolean $is_limited_display with limited operations
1275 * or not
1277 * @return string html content
1279 * @access private
1281 * @see getTable()
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')
1302 . '</div>';
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
1355 if (! $printview) {
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
1375 * @access private
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,
1387 'ORDER BY',
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'),
1396 $this->__get('db')
1399 // do we have any index?
1400 if (! empty($indexes)) {
1401 $drop_down_html = $this->_getSortByKeyDropDown(
1402 $indexes, $sort_expression,
1403 $unsorted_sql_query
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
1421 * @access private
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'))
1438 . __('Sort by key')
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) {
1446 $asc_sort = '`'
1447 . implode('` ASC, `', array_keys($index->getColumns()))
1448 . '` ASC';
1450 $desc_sort = '`'
1451 . implode('` DESC, `', array_keys($index->getColumns()))
1452 . '` DESC';
1454 $used_index = $used_index
1455 || ($local_order == $asc_sort)
1456 || ($local_order == $desc_sort);
1458 if (preg_match(
1459 '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|'
1460 . 'FOR UPDATE|LOCK IN SHARE MODE))@is',
1461 $unsorted_sql_query, $my_reg
1462 )) {
1463 $unsorted_sql_query_first_part = $my_reg[1];
1464 $unsorted_sql_query_second_part = $my_reg[2];
1465 } else {
1466 $unsorted_sql_query_first_part = $unsorted_sql_query;
1467 $unsorted_sql_query_second_part = '';
1470 $drop_down_html .= '<option value="'
1471 . htmlspecialchars(
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"'
1478 : '')
1479 . '>' . htmlspecialchars($index->getName()) . ' (ASC)</option>';
1481 $drop_down_html .= '<option value="'
1482 . htmlspecialchars(
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"'
1489 : '')
1490 . '>' . htmlspecialchars($index->getName()) . ' (DESC)</option>';
1493 $drop_down_html .= '<option value="' . htmlspecialchars($unsorted_sql_query)
1494 . '"' . ($used_index ? '' : ' selected="selected"') . '>' . __('None')
1495 . '</option>'
1496 . '</select>' . "\n"
1497 . '</form>' . "\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
1513 * @access private
1515 * @see _getTableHeaders()
1517 private function _getFieldVisibilityParams(
1518 &$displayParts, $full_or_partial_text_link
1521 $button_html = '';
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))
1529 ? ' colspan="4"'
1530 : '';
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
1547 // and required
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
1589 * @access private
1591 * @see _getTableHeaders()
1593 private function _getTableCommentsArray($analyzed_sql_results)
1595 if ((!$GLOBALS['cfg']['ShowBrowseComments'])
1596 || (empty($analyzed_sql_results['statement']->from))
1598 return array();
1601 $ret = array();
1602 foreach ($analyzed_sql_results['statement']->from as $field) {
1603 if (empty($field->table)) {
1604 continue;
1606 $ret[$field->table] = PMA_getComments(
1607 empty($field->database) ? $this->__get('db') : $field->database,
1608 $field->table
1612 return $ret;
1614 } // end of the '_getTableCommentsArray()' function
1618 * Set global array for store highlighted header fields
1620 * @param array $analyzed_sql_results analyzed sql results
1622 * @return void
1624 * @access private
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
1650 * @access private
1652 * @see _getTableHeaders()
1654 private function _getDataForResettingColumnOrder()
1657 $data_html = '';
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);
1663 if ($col_order) {
1664 $data_html .= '<input class="col_order" type="hidden" value="'
1665 . implode(',', $col_order) . '" />';
1668 $col_visib = $pmatable->getUiProp(Table::PROP_COLUMN_VISIB);
1670 if ($col_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')
1682 . '" />';
1685 return $data_html;
1687 } // end of the '_getDataForResettingColumnOrder()' function
1691 * Prepare option fields block
1693 * @return string $options_html html content
1695 * @access private
1697 * @see _getTableHeaders()
1699 private function _getOptionsBlock()
1702 $options_html = '';
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)
1719 . '<br />'
1720 . Util::getDivForSliderEffect(
1721 '', __('Options')
1723 . '<fieldset>';
1725 $options_html .= '<div class="formelement">';
1726 $choices = array(
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(
1733 'pftext', $choices,
1734 $_SESSION['tmpval']['pftext'],
1735 true, true, '', 'pftext_' . $this->__get('unique_id')
1737 . '</div>';
1739 if ($GLOBALS['cfgRelation']['relwork']
1740 && $GLOBALS['cfgRelation']['displaywork']
1742 $options_html .= '<div class="formelement">';
1743 $choices = array(
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')
1753 . '</div>';
1756 $options_html .= '<div class="formelement">'
1757 . Template::get('checkbox')
1758 ->render(
1759 array(
1760 'html_field_name' => 'display_binary',
1761 'label' => __('Show binary contents'),
1762 'checked' => ! empty($_SESSION['tmpval']['display_binary']),
1763 'onclick' => false,
1764 'html_field_id' => 'display_binary_' . $this->__get('unique_id'),
1767 . '<br />'
1768 . Template::get('checkbox')
1769 ->render(
1770 array(
1771 'html_field_name' => 'display_blob',
1772 'label' => __('Show BLOB contents'),
1773 'checked' => ! empty($_SESSION['tmpval']['display_blob']),
1774 'onclick' => false,
1775 'html_field_id' => 'display_blob_' . $this->__get('unique_id'),
1778 . '</div>';
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')
1786 ->render(
1787 array(
1788 'html_field_name' => 'hide_transformation',
1789 'label' => __('Hide browser transformation'),
1790 'checked' => ! empty($_SESSION['tmpval']['hide_transformation']),
1791 'onclick' => false,
1792 'html_field_id' => 'hide_transformation_' . $this->__get('unique_id'),
1795 . '</div>';
1797 $options_html .= '<div class="formelement">';
1798 $choices = array(
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>'
1812 . '</fieldset>';
1814 $options_html .= '<fieldset class="tblFooters">'
1815 . '<input type="submit" value="' . __('Go') . '" />'
1816 . '</fieldset>'
1817 . '</div>'
1818 . '</form>';
1820 return $options_html;
1822 } // end of the '_getOptionsBlock()' function
1826 * Get full/partial text button or link
1828 * @return string html content
1830 * @access private
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;
1850 } else {
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
1874 * @access private
1876 * @see _getTableHeaders()
1878 private function _getFormForMultiRowOperations($del_lnk)
1881 $form_html = '';
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" ';
1891 $form_html .= '>'
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') . '"';
1900 $form_html .= '>';
1902 return $form_html;
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
1915 * @access private
1917 * @see _getTableHeaders()
1919 private function _getCommentForRow($comments_map, $fields_meta)
1921 $comments = '';
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
1935 ) . '…';
1937 $comments .= $sanitized_comments;
1938 $comments .= '</span>';
1940 return $comments;
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
1961 * @access private
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)
1982 ? Util::backquote(
1983 $fields_meta->table
1984 ) . '.'
1985 : '';
1987 $name_to_use_in_sort = $fields_meta->name;
1989 // Generates the orderby clause part of the query which is part
1990 // of URL
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
1997 if (preg_match(
1998 '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|'
1999 . 'LOCK IN SHARE MODE))@is',
2000 $unsorted_sql_query, $regs3
2001 )) {
2002 $single_sorted_sql_query = $regs3[1] . $single_sort_order . $regs3[2];
2003 $multi_sorted_sql_query = $regs3[1] . $multi_sort_order . $regs3[2];
2004 } else {
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
2051 * consideration
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
2058 * @access private
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
2066 $sort_order = "";
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]
2078 = Util::backquote(
2079 $current_name
2081 $sort_direction[$special_index] = (preg_match(
2082 '@time|date@i',
2083 $fields_meta->type
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 . ' ' ;
2119 } else {
2120 if (strlen($sort_tbl_new) > 0) {
2121 $sort_tbl_new .= ".";
2123 $sort_order .= $query_head . $sort_tbl_new
2124 . Util::backquote(
2125 $name_to_use_in_sort
2126 ) . ' ' ;
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 . ' ';
2136 } else {
2137 $single_sort_order = "\n" . 'ORDER BY ' . $sort_tbl
2138 . Util::backquote(
2139 $current_name
2140 ) . ' ';
2142 if ($is_in_sort) {
2143 list($single_sort_order, $order_img)
2144 = $this->_getSortingUrlParams(
2145 $sort_direction, $single_sort_order,
2146 $column_index, $index
2148 } else {
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>";
2158 } else {
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(
2169 $sort_order,
2171 mb_strlen($sort_order) - 2
2173 if (empty($order_img)) {
2174 $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
2189 * @access private
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]);
2204 } else {
2205 $clause2 = $sort_tbl . str_replace('`', '', $clause);
2207 if ($clause2 === $sort_tbl . $name_to_use_in_sort) {
2208 $index_in_expression = $index;
2209 break;
2212 if (empty($sort_expression[$index_in_expression])) {
2213 $is_in_sort = false;
2214 } else {
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
2226 ) === false;
2227 $noOpenParenthesis = mb_strpos(
2228 $sort_expression_nodirection[$index_in_expression], '('
2229 ) === false;
2230 if (! empty($sort_tbl) && $noSortTable && $noOpenParenthesis) {
2231 $new_sort_expression_nodirection = $sort_tbl
2232 . $sort_expression_nodirection[$index_in_expression];
2233 } else {
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
2239 //to compare.
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])
2248 $is_in_sort = true;
2252 return $is_in_sort;
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
2267 * @access private
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' => '')
2284 } else {
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
2310 * @access private
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
2347 * @return void
2349 * @see _getDraggableClassForSortableColumns()
2351 private function _getClassForNumericColumnType($fields_meta,&$th_class)
2353 if (preg_match(
2354 '@int|decimal|float|double|real|bit|boolean|serial@i',
2355 $fields_meta->type
2356 )) {
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
2373 * @access private
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
2421 * @access private
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
2465 * @access private
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
2492 . '</th>';
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
2508 . '></td>';
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
2527 * @access private
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
2549 * @access private
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="'
2561 . $this->_addClass(
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
2578 * @access private
2580 * @see _getDataCellForNumericColumns(),
2581 * _getDataCellForGeometryColumns(),
2582 * _getDataCellForNonNumericColumns()
2584 private function _buildEmptyDisplay($class, $condition_field, $meta, $align = '')
2586 return '<td ' . $align . ' class="'
2587 . $this->_addClass(
2588 $class, $condition_field, $meta, 'nowrap'
2590 . '"></td>';
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
2599 * condition
2600 * @param object $meta the meta-information about the
2601 * field
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
2611 * @access private
2613 * @see _buildNullDisplay(), _getRowData()
2615 private function _addClass(
2616 $class, $condition_field, $meta, $nowrap, $is_field_truncated = false,
2617 $transformation_plugin = '', $default_function = ''
2619 $classes = array(
2620 $class,
2621 $nowrap,
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
2646 $matches = array(
2647 'enum' => 'enum',
2648 'set' => 'set',
2649 'binary' => 'hex',
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) {
2659 $classes[] = 'bit';
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
2680 * @access private
2682 * @see getTable()
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)) {
2701 $map = array();
2704 $row_no = 0;
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 = '';
2717 } else {
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';
2723 break;
2724 case 'click':
2725 $grid_edit_class = 'grid_edit click1';
2726 break;
2727 default: // 'disabled'
2728 $grid_edit_class = '';
2729 break;
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
2741 // tables.
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(
2758 $display_params
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
2798 $row, // row
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,
2812 $edit_anchor_class)
2813 = $this->_getModifiedLinks(
2814 $where_clause,
2815 $clause_is_unique, $url_sql_query
2818 } // end if (1.2.1)
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'],
2825 $row
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
2849 } // end if (1.3)
2850 } // end if (1)
2852 // 2. Displays the rows' values
2853 if (is_null($this->__get('mime_map'))) {
2854 $this->_setMimeMap();
2856 $table_body_html .= $this->_getRowValues(
2857 $dt_result,
2858 $row,
2859 $row_no,
2860 $col_order,
2861 $map,
2862 $grid_edit_class,
2863 $col_visib,
2864 $url_sql_query,
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
2884 } // end if (3)
2886 $table_body_html .= '</tr>';
2887 $table_body_html .= "\n";
2888 $row_no++;
2890 } // end while
2892 return $table_body_html;
2894 } // end of the '_getTableBody()' function
2897 * Sets the MIME details of the columns in the results set
2899 * @return void
2901 private function _setMimeMap()
2903 $fields_meta = $this->__get('fields_meta');
2904 $mimeMap = array();
2905 $added = array();
2907 for ($currentColumn = 0;
2908 $currentColumn < $this->__get('fields_cnt');
2909 ++$currentColumn) {
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(
2921 $mimeMap,
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']
2932 preg_match(
2933 '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
2934 . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
2935 . ')@i',
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
2971 * displayed
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
2979 * columns
2980 * @param boolean|array|string $col_visib column is visible(false);
2981 * column isn't visible(string
2982 * array)
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
2988 * @access private
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;
3013 ++$currentColumn) {
3015 // assign $i with appropriate column order
3016 $i = $col_order ? $col_order[$currentColumn] : $currentColumn;
3018 $meta = $fields_meta[$i];
3019 $orgFullColName
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])
3025 ? 'hide'
3026 : '';
3027 $grid_edit = $meta->orgtable != '' ? $grid_edit_class : '';
3029 // handle datetime-related class, for grid editing
3030 $field_type_class
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
3041 // where-query.
3042 $condition_field = (isset($highlight_columns)
3043 && (isset($highlight_columns[$meta->name])
3044 || isset($highlight_columns[Util::backquote($meta->name)])))
3045 ? true
3046 : false;
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(
3071 $plugin_manager
3074 $transform_options = PMA_Transformation_getOptions(
3075 isset(
3076 $mime_map[$orgFullColName]
3077 ['transformation_options']
3079 ? $mime_map[$orgFullColName]
3080 ['transformation_options']
3081 : ''
3084 $meta->mimetype = str_replace(
3085 '_', '/',
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']
3110 : ''
3113 $meta->mimetype = str_replace(
3114 '_', '/',
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(
3135 0 => $linking_url,
3136 2 => true
3139 $meta->mimetype = str_replace(
3140 '_', '/',
3141 'Text/Plain'
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
3158 $row, // row
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') {
3184 // n u m e r i c
3186 $display_params['data'][$row_no][$i]
3187 = $this->_getDataCellForNumericColumns(
3188 $row[$i],
3189 $class,
3190 $condition_field,
3191 $meta,
3192 $map,
3193 $is_field_truncated,
3194 $analyzed_sql_results,
3195 $transformation_plugin,
3196 $default_function,
3197 $transform_options
3200 } elseif ($meta->type == self::GEOMETRY_FIELD) {
3201 // g e o m e t r y
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(
3209 $row[$i],
3210 $class,
3211 $meta,
3212 $map,
3213 $_url_params,
3214 $condition_field,
3215 $transformation_plugin,
3216 $default_function,
3217 $transform_options,
3218 $analyzed_sql_results
3221 } else {
3222 // n o t n u m e r i c
3224 $display_params['data'][$row_no][$i]
3225 = $this->_getDataCellForNonNumericColumns(
3226 $row[$i],
3227 $class,
3228 $meta,
3229 $map,
3230 $_url_params,
3231 $condition_field,
3232 $transformation_plugin,
3233 $default_function,
3234 $transform_options,
3235 $is_field_truncated,
3236 $analyzed_sql_results,
3237 $dt_result,
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];
3249 } else {
3250 $display_params['rowdata'][$i][$row_no]
3251 = $display_params['data'][$row_no][$i];
3254 $this->__set('display_params', $display_params);
3256 } // end for
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'))]
3278 [$field_name];
3280 if (! is_array($link_relations['link_param'])) {
3281 $linking_url_params[$link_relations['link_param']] = $column_value;
3282 } else {
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
3300 // from that array
3301 if (is_array($new_param['param_info'])) {
3302 $linking_url_params[$new_param['param_info'][0]]
3303 = $new_param['param_info'][1];
3304 continue;
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'])) {
3313 continue;
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)]
3339 = $row[$m];
3342 return $row_info;
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
3353 * @access private
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,
3368 'SELECT'
3371 $from_clause = Query::getClause(
3372 $analyzed_sql_results['statement'],
3373 $analyzed_sql_results['parser']->list,
3374 'FROM'
3377 if (!empty($from_clause)) {
3378 $query .= ' FROM ' . $from_clause;
3381 return $query;
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
3393 * @access private
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);
3403 } else {
3404 $col_order = false;
3405 $col_visib = false;
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
3419 * @access private
3421 * @see _getTableBody()
3423 private function _getRepeatingHeaders(
3424 $display_params
3426 $header_html = '<tr>' . "\n";
3428 if ($display_params['emptypre'] > 0) {
3430 $header_html .= ' <th colspan="'
3431 . $display_params['emptypre'] . '">'
3432 . "\n" . ' &nbsp;</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) {
3443 $header_html
3444 .= ' <th colspan="' . $display_params['emptyafter']
3445 . '">'
3446 . "\n" . ' &nbsp;</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
3465 * @access private
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'
3483 . URL::getCommon(
3484 $_url_params + array('default_action' => 'update')
3487 $copy_url = 'tbl_change.php'
3488 . URL::getCommon(
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
3522 * @access private
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(
3578 'db' => 'mysql',
3579 'sql_query' => $kill,
3580 'goto' => $lnk_goto,
3583 $del_url = 'sql.php' . URL::getCommon($_url_params);
3584 $js_conf = $kill;
3585 $del_str = Util::getIcon(
3586 'b_drop.png', __('Kill')
3588 } else {
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
3603 * @return string
3605 * @access private
3607 * @see _getModifiedLinks(), _getDeleteAndKillLinks()
3609 private function _getActionLinkContent($icon, $display_text)
3612 $linkContent = '';
3614 if (isset($GLOBALS['cfg']['RowActionType'])
3615 && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_ICONS
3618 $linkContent .= '<span class="nowrap">'
3619 . Util::getImage(
3620 $icon, $display_text
3622 . '</span>';
3624 } else if (isset($GLOBALS['cfg']['RowActionType'])
3625 && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_TEXT
3628 $linkContent .= '<span class="nowrap">' . $display_text . '</span>';
3630 } else {
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
3663 * @access private
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)) {
3674 $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
3698 * @access private
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;
3709 return $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
3721 * @access private
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';
3737 } else {
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
3750 * or not
3751 * @param object $meta the meta-information about this
3752 * field
3753 * @param array $map the list of relations
3754 * @param boolean $is_field_truncated the condition for blob data
3755 * replacements
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
3759 * function
3760 * @param array $transform_options the transformation parameters
3762 * @return string $cell the prepared cell, html content
3764 * @access private
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,
3771 $transform_options
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, ''
3792 } else {
3794 $cell = $this->_buildEmptyDisplay(
3795 'right ' . $class, $condition_field, $meta, ''
3799 return $cell;
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
3810 * this field
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
3814 * or not
3815 * @param object|string $transformation_plugin the name of transformation
3816 * function
3817 * @param string $default_function the default transformation
3818 * function
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
3824 * @access private
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);
3835 return $cell;
3838 if ($column == '') {
3839 $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
3840 return $cell;
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
3853 return $cell;
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);
3862 list(
3863 $is_field_truncated,
3864 $wktval,
3865 // skip 3rd param
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, ''
3874 return $cell;
3877 // Prepare in Well Known Binary (WKB) format.
3879 if ($_SESSION['tmpval']['display_binary']) {
3880 $where_comparison = ' = ' . $column;
3882 $wkbval = substr(bin2hex($column), 8);
3883 list(
3884 $is_field_truncated,
3885 $wkbval,
3886 // skip 3rd param
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, ''
3896 return $cell;
3899 $wkbval = $this->_handleNonPrintableContents(
3900 self::BINARY_FIELD, $column, $transformation_plugin,
3901 $transform_options, $default_function, $meta,
3902 $_url_params
3905 $cell = $this->_buildValueDisplay(
3906 $class, $condition_field, $wkbval
3909 return $cell;
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
3920 * the field
3921 * @param array $map the list of relations
3922 * @param array $_url_params the parameters for generate
3923 * url
3924 * @param boolean $condition_field the column should highlighted
3925 * or not
3926 * @param object|string $transformation_plugin the name of transformation
3927 * function
3928 * @param string $default_function the default transformation
3929 * function
3930 * @param string $transform_options the transformation parameters
3931 * @param boolean $is_field_truncated is data truncated due to
3932 * LimitChars
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
3941 * @access private
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')
3957 === false;
3959 // disable inline grid editing
3960 // if binary fields are protected
3961 // or transformation plugin is of non text type
3962 // such as image
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))))
3969 || $bIsText
3971 $class = str_replace('grid_edit', '', $class);
3974 if (! isset($column) || is_null($column)) {
3975 $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
3976 return $cell;
3979 if ($column == '') {
3980 $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
3981 return $cell;
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)
3990 list(
3991 $is_field_truncated,
3992 $column,
3993 $original_length
3994 ) = $this->_getPartialText($column);
3997 $formatted = false;
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);
4031 $formatted = true;
4034 if ($formatted) {
4035 $cell = $this->_buildValueDisplay(
4036 $class, $condition_field, $column
4038 return $cell;
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)
4047 : false;
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)
4055 . '\'';
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
4065 return $cell;
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!?
4078 * @return void
4080 * @access public
4082 * @see sql.php file
4084 public function setConfigParamsForDisplayTable()
4087 $sql_md5 = md5($this->__get('sql_query'));
4088 $query = array();
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'])) {
4115 $query['pos'] = 0;
4118 if (PMA_isValid(
4119 $_REQUEST['pftext'],
4120 array(
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;
4131 if (PMA_isValid(
4132 $_REQUEST['relational_display'],
4133 array(
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'];
4147 if (PMA_isValid(
4148 $_REQUEST['geoOption'],
4149 array(
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
4169 } else {
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']
4206 = $query['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']
4221 = $query['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
4239 * @access public
4241 * @see sql.php file
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'];
4254 $table_html = '';
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);
4271 if ($is_innodb
4272 && PMA_isJustBrowsing($analyzed_sql_results, true)
4274 // "j u s t b r o w s i n g"
4275 $pre_count = '~';
4276 $after_count = Util::showHint(
4277 Sanitize::sanitize(
4278 __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc].')
4281 } else {
4282 $pre_count = '';
4283 $after_count = '';
4286 // 1. ----- Prepares the work -----
4288 // 1.1 Gets the information about which functionalities should be
4289 // displayed
4291 list(
4292 $displayParts,
4293 $total
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();
4299 } // end if
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;
4314 } else {
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,
4338 $total,
4339 $pos_next,
4340 $pre_count,
4341 $after_count
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);
4364 } else {
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]
4382 } else {
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)
4396 // initialize map
4397 $map = array();
4399 $target = array();
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,
4419 $this->__get('db')
4422 } // end if
4423 // end 2b
4425 // 3. ----- Prepare the results table -----
4426 if ($is_limited_display) {
4427 $table_html .= "<br>";
4430 $table_html .= $this->_getTableHeaders(
4431 $displayParts,
4432 $analyzed_sql_results,
4433 $unsorted_sql_query,
4434 $sort_expression,
4435 $sort_expression_nodirection,
4436 $sort_direction,
4437 $is_limited_display
4440 $table_html .= '<tbody>' . "\n";
4442 $table_html .= $this->_getTableBody(
4443 $dt_result,
4444 $displayParts,
4445 $map,
4446 $analyzed_sql_results,
4447 $is_limited_display
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(
4461 $dt_result,
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
4485 return $table_html;
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
4495 * @access private
4497 * @see getTable()
4499 private function _getOffsets()
4502 if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
4503 $pos_next = 0;
4504 $pos_prev = 0;
4505 } else {
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) {
4514 $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
4528 * be displayed
4529 * @param string $sort_expression_nodirection sort expression without direction
4531 * @return string html content
4532 * null if not found sorted column
4534 * @access private
4536 * @see getTable()
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)) {
4545 return null;
4548 if (mb_strpos($sort_expression_nodirection, '.') === false) {
4549 $sort_table = $this->__get('table');
4550 $sort_column = $sort_expression_nodirection;
4551 } else {
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;
4566 break;
4570 if ($sorted_column_index === false) {
4571 return null;
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
4595 } else {
4596 $column_for_first_row = $row[$sorted_column_index];
4599 $column_for_first_row = mb_strtoupper(
4600 mb_substr(
4601 $column_for_first_row, 0, $GLOBALS['cfg']['LimitChars']
4602 ) . '...'
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
4621 } else {
4622 $column_for_last_row = $row[$sorted_column_index];
4625 $column_for_last_row = mb_strtoupper(
4626 mb_substr(
4627 $column_for_last_row, 0, $GLOBALS['cfg']['LimitChars']
4628 ) . '...'
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
4655 * @access private
4657 * @see getTable()
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;
4673 } else {
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;
4684 } else {
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);
4707 } else {
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);
4716 } else {
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);
4730 } else {
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, '');
4751 return $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
4761 * @return void
4763 * @access private
4765 * @see getTable()
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'],
4790 $display_field,
4791 $rel['foreign_db']
4793 } else {
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']
4799 : $GLOBALS['db'],
4800 $one_key['ref_table_name']
4803 $map[$one_field] = array(
4804 $one_key['ref_table_name'],
4805 $one_key['ref_index_list'][$index],
4806 $display_field,
4807 isset($one_key['ref_db_name'])
4808 ? $one_key['ref_db_name']
4809 : $GLOBALS['db']
4814 } // end while
4815 } // end if
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
4830 * @access private
4832 * @see getTable()
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')) . '" />'
4880 . "\n";
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
4898 $row, // row
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";
4913 return $links_html;
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
4929 * @access private
4931 * @see _getTable()
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
4961 * @return string
4963 * @access private
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,
4977 Util::getIcon(
4978 'b_view_add.png', __('Create view'), true
4980 array('class' => 'create_view' . $ajax_class), true, true, ''
4982 . '</span>' . "\n";
4984 return $results_operations_html;
4989 * Calls the _getResultsOperations with $only_view as true
4991 * @param array $analyzed_sql_results analyzed sql results
4993 * @return string
4995 * @access public
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(
5005 array(),
5006 $analyzed_sql_results,
5007 true
5009 return $results_operations_html;
5013 * Get copy to clipboard links for results operations
5015 * @return string $html
5017 * @access private
5019 private function _getCopytoclipboardLinks()
5021 $html = Util::linkOrButton(
5022 '#',
5023 Util::getIcon(
5024 'b_insrow.png', __('Copy to clipboard'), true
5026 array('id' => 'copyToClipBoard'),
5027 true,
5028 true,
5029 'copy_to_clip_board'
5032 return $html;
5036 * Get printview links for results operations
5038 * @return string $html
5040 * @access private
5042 private function _getPrintviewLinks()
5044 $html = Util::linkOrButton(
5045 '#',
5046 Util::getIcon(
5047 'b_print.png', __('Print'), true
5049 array('id' => 'printView'),
5050 true,
5051 true,
5052 'print_view'
5055 return $html;
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
5067 * @access private
5069 * @see getTable()
5071 private function _getResultsOperations(
5072 $displayParts, $analyzed_sql_results, $only_view = false
5074 global $printview;
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'),
5085 'printview' => '1',
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
5096 if ($only_view) {
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"
5113 // Export link
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),
5154 Util::getIcon(
5155 'b_tblexport.png', __('Export'), true
5158 true,
5159 true,
5162 . "\n";
5164 // prepare chart
5165 $results_operations_html .= Util::linkOrButton(
5166 'tbl_chart.php' . URL::getCommon($_url_params),
5167 Util::getIcon(
5168 'b_chart.png', __('Display chart'), true
5171 true,
5172 true,
5175 . "\n";
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;
5183 break;
5187 if ($geometry_found) {
5188 $results_operations_html
5189 .= Util::linkOrButton(
5190 'tbl_gis_visualization.php'
5191 . URL::getCommon($_url_params),
5192 Util::getIcon(
5193 'b_globe.gif', __('Visualize GIS data'), true
5196 true,
5197 true,
5200 . "\n";
5204 // CREATE VIEW
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)
5232 * in Browse mode.
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
5243 * download link
5244 * @param boolean &$is_truncated the result is truncated or not
5246 * @return mixed string or float
5248 * @access private
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];
5268 } else {
5270 $result .= ' - NULL';
5271 $size = 0;
5275 $result .= ']';
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(),
5281 'Octetstream'
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));
5293 if ($size <= 0) {
5294 return($result);
5297 if ($default_function != $transformation_plugin) {
5298 $result = $transformation_plugin->applyTransformation(
5299 $result,
5300 $transform_options,
5301 $meta
5303 return($result);
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);
5318 } else {
5319 $result = '0x' . bin2hex($content);
5321 list(
5322 $is_truncated,
5323 $result,
5324 // skip 3rd param
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">'
5338 . $result . '</a>';
5341 return($result);
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
5355 * @access private
5358 private function _getFromForeign($map, $meta, $where_comparison)
5360 $dispsql = 'SELECT '
5361 . Util::backquote($map[$meta->name][2])
5362 . ' FROM '
5363 . Util::backquote($map[$meta->name][3])
5364 . '.'
5365 . Util::backquote($map[$meta->name][0])
5366 . ' WHERE '
5367 . Util::backquote($map[$meta->name][1])
5368 . $where_comparison;
5370 $dispresult = $GLOBALS['dbi']->tryQuery(
5371 $dispsql,
5372 null,
5373 DatabaseInterface::QUERY_STORE
5376 if ($dispresult && $GLOBALS['dbi']->numRows($dispresult) > 0) {
5377 list($dispval) = $GLOBALS['dbi']->fetchRow($dispresult, 0);
5378 } else {
5379 $dispval = __('Link not found!');
5382 $GLOBALS['dbi']->freeResult($dispresult);
5384 return $dispval;
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
5393 * the where clause
5394 * @param array $analyzed_sql_results the analyzed query
5395 * @param object $meta the meta-information about the
5396 * field
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
5404 * not be wrapped
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
5412 * @access private
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="'
5435 . $this->_addClass(
5436 $class, $condition_field, $meta, $nowrap,
5437 $is_field_truncated, $transformation_plugin, $default_function
5439 . '">';
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))) {
5444 continue;
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
5461 } else {
5462 $dispval = '';
5463 } // end if... else...
5465 if (isset($printview) && ($printview == '1')) {
5467 $result .= ($transformation_plugin != $default_function
5468 ? $transformation_plugin->applyTransformation(
5469 $data,
5470 $transform_options,
5471 $meta
5473 : $default_function($data)
5475 . ' <code>[-&gt;' . $dispval . ']</code>';
5477 } else {
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) . '"'
5485 : '';
5487 } else {
5488 $title = ' title="' . htmlspecialchars($data) . '"';
5491 $_url_params = array(
5492 'db' => $map[$meta->name][3],
5493 'table' => $map[$meta->name][0],
5494 'pos' => '0',
5495 'sql_query' => 'SELECT * FROM '
5496 . Util::backquote($map[$meta->name][3]) . '.'
5497 . Util::backquote($map[$meta->name][0])
5498 . ' WHERE '
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(
5511 $data,
5512 $transform_options,
5513 $meta
5515 } else {
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);
5523 } else {
5524 // otherwise display data in the cell
5525 $result .= $default_function($data);
5529 $result .= '</a>';
5532 } else {
5533 $result .= ($transformation_plugin != $default_function
5534 ? $transformation_plugin->applyTransformation(
5535 $data,
5536 $transform_options,
5537 $meta
5539 : $default_function($data)
5543 $result .= '</td>' . "\n";
5545 return $result;
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
5564 * @access private
5566 * @see _getTableBody(), _getCheckboxAndLinks()
5568 private function _getCheckboxForMultiRowSubmissions(
5569 $del_url, $displayParts, $row_no, $where_clause_html, $condition_array,
5570 $id_suffix, $class
5573 $ret = '';
5575 if (! empty($del_url) && $displayParts['del_lnk'] != self::KILL_PROCESS) {
5577 $ret .= '<td ';
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 . '" '
5588 . ' />'
5589 . '<input type="hidden" class="condition_array" value="'
5590 . htmlspecialchars(json_encode($condition_array)) . '" />'
5591 . ' </td>';
5594 return $ret;
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
5610 * @access private
5612 * @see _getTableBody(), _getCheckboxAndLinks()
5614 private function _getEditLink(
5615 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5618 $ret = '';
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>';
5637 return $ret;
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
5653 * @access private
5655 * @see _getTableBody(), _getCheckboxAndLinks()
5657 private function _getCopyLink(
5658 $copy_url, $copy_str, $where_clause, $where_clause_html, $class
5661 $ret = '';
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>';
5685 return $ret;
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
5700 * @access private
5702 * @see _getTableBody(), _getCheckboxAndLinks()
5704 private function _getDeleteLink($del_url, $del_str, $js_conf, $class)
5707 $ret = '';
5708 if (empty($del_url)) {
5709 return $ret;
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(
5719 $del_url,
5720 $del_str,
5721 array('class' => 'delete_row requireConfirm' . $ajax),
5722 false
5724 . '<div class="hide">' . $js_conf . '</div>'
5725 . '</td>';
5727 return $ret;
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
5739 * display elements
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
5754 * @access private
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
5764 $ret = '';
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', ''
5808 return $ret;
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
5819 * @return mixed
5821 * @access private
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
5832 $str = mb_substr(
5833 $str, 0, $GLOBALS['cfg']['LimitChars']
5834 ) . '...';
5835 $truncated = true;
5836 } else {
5837 $truncated = false;
5840 return array($truncated, $str, $original_length);