Merge remote-tracking branch 'origin/master'
[phpmyadmin.git] / libraries / DisplayResults.php
blobde5b495eb31aec1e3d2770b8b0f772f08d8642cc
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 SqlParser\Utils\Query;
11 use PMA\libraries\plugins\transformations\Text_Plain_Link;
13 require_once './libraries/transformations.lib.php';
15 /**
16 * Handle all the functionalities related to displaying results
17 * of sql queries, stored procedure, browsing sql processes or
18 * displaying binary log.
20 * @package PhpMyAdmin
22 class DisplayResults
25 // Define constants
26 const NO_EDIT_OR_DELETE = 'nn';
27 const UPDATE_ROW = 'ur';
28 const DELETE_ROW = 'dr';
29 const KILL_PROCESS = 'kp';
31 const POSITION_LEFT = 'left';
32 const POSITION_RIGHT = 'right';
33 const POSITION_BOTH = 'both';
34 const POSITION_NONE = 'none';
36 const PLACE_TOP_DIRECTION_DROPDOWN = 'top_direction_dropdown';
37 const PLACE_BOTTOM_DIRECTION_DROPDOWN = 'bottom_direction_dropdown';
39 const DISPLAY_FULL_TEXT = 'F';
40 const DISPLAY_PARTIAL_TEXT = 'P';
42 const HEADER_FLIP_TYPE_AUTO = 'auto';
43 const HEADER_FLIP_TYPE_CSS = 'css';
44 const HEADER_FLIP_TYPE_FAKE = 'fake';
46 const DATE_FIELD = 'date';
47 const DATETIME_FIELD = 'datetime';
48 const TIMESTAMP_FIELD = 'timestamp';
49 const TIME_FIELD = 'time';
50 const STRING_FIELD = 'string';
51 const GEOMETRY_FIELD = 'geometry';
52 const BLOB_FIELD = 'BLOB';
53 const BINARY_FIELD = 'BINARY';
55 const RELATIONAL_KEY = 'K';
56 const RELATIONAL_DISPLAY_COLUMN = 'D';
58 const GEOMETRY_DISP_GEOM = 'GEOM';
59 const GEOMETRY_DISP_WKT = 'WKT';
60 const GEOMETRY_DISP_WKB = 'WKB';
62 const SMART_SORT_ORDER = 'SMART';
63 const ASCENDING_SORT_DIR = 'ASC';
64 const DESCENDING_SORT_DIR = 'DESC';
66 const TABLE_TYPE_INNO_DB = 'InnoDB';
67 const ALL_ROWS = 'all';
68 const QUERY_TYPE_SELECT = 'SELECT';
70 const ROUTINE_PROCEDURE = 'procedure';
71 const ROUTINE_FUNCTION = 'function';
73 const ACTION_LINK_CONTENT_ICONS = 'icons';
74 const ACTION_LINK_CONTENT_TEXT = 'text';
77 // Declare global fields
79 /** array with properties of the class */
80 private $_property_array = array(
82 /** string Database name */
83 'db' => null,
85 /** string Table name */
86 'table' => null,
88 /** string the URL to go back in case of errors */
89 'goto' => null,
91 /** string the SQL query */
92 'sql_query' => null,
94 /**
95 * integer the total number of rows returned by the SQL query without any
96 * appended "LIMIT" clause programmatically
98 'unlim_num_rows' => null,
100 /** array meta information about fields */
101 'fields_meta' => null,
103 /** boolean */
104 'is_count' => null,
106 /** integer */
107 'is_export' => null,
109 /** boolean */
110 'is_func' => null,
112 /** integer */
113 'is_analyse' => null,
115 /** integer the total number of rows returned by the SQL query */
116 'num_rows' => null,
118 /** integer the total number of fields returned by the SQL query */
119 'fields_cnt' => null,
121 /** double time taken for execute the SQL query */
122 'querytime' => null,
124 /** string path for theme images directory */
125 'pma_theme_image' => null,
127 /** string */
128 'text_dir' => null,
130 /** boolean */
131 'is_maint' => null,
133 /** boolean */
134 'is_explain' => null,
136 /** boolean */
137 'is_show' => null,
139 /** boolean */
140 'is_browse_distinct' => null,
142 /** array table definitions */
143 'showtable' => null,
145 /** string */
146 'printview' => null,
148 /** string URL query */
149 'url_query' => null,
151 /** array column names to highlight */
152 'highlight_columns' => null,
154 /** array holding various display information */
155 'display_params' => null,
157 /** array mime types information of fields */
158 'mime_map' => null,
160 /** boolean */
161 'editable' => null,
163 /** random unique ID to distinguish result set */
164 'unique_id' => null,
166 /** where clauses for each row, each table in the row */
167 'whereClauseMap' => array(),
171 * This variable contains the column transformation information
172 * for some of the system databases.
173 * One element of this array represent all relevant columns in all tables in
174 * one specific database
176 public $transformation_info;
180 * Get any property of this class
182 * @param string $property name of the property
184 * @return mixed|void if property exist, value of the relevant property
186 public function __get($property)
188 if (array_key_exists($property, $this->_property_array)) {
189 return $this->_property_array[$property];
195 * Set values for any property of this class
197 * @param string $property name of the property
198 * @param mixed $value value to set
200 * @return void
202 public function __set($property, $value)
204 if (array_key_exists($property, $this->_property_array)) {
205 $this->_property_array[$property] = $value;
211 * Constructor for DisplayResults class
213 * @param string $db the database name
214 * @param string $table the table name
215 * @param string $goto the URL to go back in case of errors
216 * @param string $sql_query the SQL query
218 * @access public
220 public function __construct($db, $table, $goto, $sql_query)
222 $this->_setDefaultTransformations();
224 $this->__set('db', $db);
225 $this->__set('table', $table);
226 $this->__set('goto', $goto);
227 $this->__set('sql_query', $sql_query);
228 $this->__set('unique_id', rand());
232 * Sets default transformations for some columns
234 * @return void
236 private function _setDefaultTransformations()
238 $json_highlighting_data = array(
239 'libraries/plugins/transformations/output/Text_Plain_Json.php',
240 'PMA\libraries\plugins\transformations\output\Text_Plain_Json',
241 'Text_Plain'
243 $sql_highlighting_data = array(
244 'libraries/plugins/transformations/output/Text_Plain_Sql.php',
245 'PMA\libraries\plugins\transformations\output\Text_Plain_Sql',
246 'Text_Plain'
248 $blob_sql_highlighting_data = array(
249 'libraries/plugins/transformations/output/Text_Octetstream_Sql.php',
250 'PMA\libraries\plugins\transformations\output\Text_Octetstream_Sql',
251 'Text_Octetstream'
253 $link_data = array(
254 'libraries/plugins/transformations/Text_Plain_Link.php',
255 'PMA\libraries\plugins\transformations\Text_Plain_Link',
256 'Text_Plain'
258 $this->transformation_info = array(
259 'information_schema' => array(
260 'events' => array(
261 'event_definition' => $sql_highlighting_data
263 'processlist' => array(
264 'info' => $sql_highlighting_data
266 'routines' => array(
267 'routine_definition' => $sql_highlighting_data
269 'triggers' => array(
270 'action_statement' => $sql_highlighting_data
272 'views' => array(
273 'view_definition' => $sql_highlighting_data
276 'mysql' => array(
277 'event' => array(
278 'body' => $blob_sql_highlighting_data,
279 'body_utf8' => $blob_sql_highlighting_data
281 'general_log' => array(
282 'argument' => $sql_highlighting_data
284 'help_category' => array(
285 'url' => $link_data
287 'help_topic' => array(
288 'example' => $sql_highlighting_data,
289 'url' => $link_data
291 'proc' => array(
292 'param_list' => $blob_sql_highlighting_data,
293 'returns' => $blob_sql_highlighting_data,
294 'body' => $blob_sql_highlighting_data,
295 'body_utf8' => $blob_sql_highlighting_data
297 'slow_log' => array(
298 'sql_text' => $sql_highlighting_data
303 $cfgRelation = PMA_getRelationsParam();
304 if ($cfgRelation['db']) {
305 $this->transformation_info[$cfgRelation['db']] = array();
306 $relDb = &$this->transformation_info[$cfgRelation['db']];
307 if (! empty($cfgRelation['history'])) {
308 $relDb[$cfgRelation['history']] = array(
309 'sqlquery' => $sql_highlighting_data
312 if (! empty($cfgRelation['bookmark'])) {
313 $relDb[$cfgRelation['bookmark']] = array(
314 'query' => $sql_highlighting_data
317 if (! empty($cfgRelation['tracking'])) {
318 $relDb[$cfgRelation['tracking']] = array(
319 'schema_sql' => $sql_highlighting_data,
320 'data_sql' => $sql_highlighting_data
323 if (! empty($cfgRelation['favorite'])) {
324 $relDb[$cfgRelation['favorite']] = array(
325 'tables' => $json_highlighting_data
328 if (! empty($cfgRelation['recent'])) {
329 $relDb[$cfgRelation['recent']] = array(
330 'tables' => $json_highlighting_data
333 if (! empty($cfgRelation['savedsearches'])) {
334 $relDb[$cfgRelation['savedsearches']] = array(
335 'search_data' => $json_highlighting_data
338 if (! empty($cfgRelation['designer_settings'])) {
339 $relDb[$cfgRelation['designer_settings']] = array(
340 'settings_data' => $json_highlighting_data
343 if (! empty($cfgRelation['table_uiprefs'])) {
344 $relDb[$cfgRelation['table_uiprefs']] = array(
345 'prefs' => $json_highlighting_data
348 if (! empty($cfgRelation['userconfig'])) {
349 $relDb[$cfgRelation['userconfig']] = array(
350 'config_data' => $json_highlighting_data
353 if (! empty($cfgRelation['export_templates'])) {
354 $relDb[$cfgRelation['export_templates']] = array(
355 'template_data' => $json_highlighting_data
362 * Set properties which were not initialized at the constructor
364 * @param integer $unlim_num_rows the total number of rows returned by
365 * the SQL query without any appended
366 * "LIMIT" clause programmatically
367 * @param array $fields_meta meta information about fields
368 * @param boolean $is_count statement is SELECT COUNT
369 * @param integer $is_export statement contains INTO OUTFILE
370 * @param boolean $is_func statement contains a function like SUM()
371 * @param integer $is_analyse statement contains PROCEDURE ANALYSE
372 * @param integer $num_rows total no. of rows returned by SQL query
373 * @param integer $fields_cnt total no.of fields returned by SQL query
374 * @param double $querytime time taken for execute the SQL query
375 * @param string $pmaThemeImage path for theme images directory
376 * @param string $text_dir text direction
377 * @param boolean $is_maint statement contains a maintenance command
378 * @param boolean $is_explain statement contains EXPLAIN
379 * @param boolean $is_show statement contains SHOW
380 * @param array $showtable table definitions
381 * @param string $printview print view was requested
382 * @param string $url_query URL query
383 * @param boolean $editable whether the results set is editable
384 * @param boolean $is_browse_dist whether browsing distinct values
386 * @return void
388 * @see sql.php
390 public function setProperties(
391 $unlim_num_rows, $fields_meta, $is_count, $is_export, $is_func,
392 $is_analyse, $num_rows, $fields_cnt, $querytime, $pmaThemeImage, $text_dir,
393 $is_maint, $is_explain, $is_show, $showtable, $printview, $url_query,
394 $editable, $is_browse_dist
397 $this->__set('unlim_num_rows', $unlim_num_rows);
398 $this->__set('fields_meta', $fields_meta);
399 $this->__set('is_count', $is_count);
400 $this->__set('is_export', $is_export);
401 $this->__set('is_func', $is_func);
402 $this->__set('is_analyse', $is_analyse);
403 $this->__set('num_rows', $num_rows);
404 $this->__set('fields_cnt', $fields_cnt);
405 $this->__set('querytime', $querytime);
406 $this->__set('pma_theme_image', $pmaThemeImage);
407 $this->__set('text_dir', $text_dir);
408 $this->__set('is_maint', $is_maint);
409 $this->__set('is_explain', $is_explain);
410 $this->__set('is_show', $is_show);
411 $this->__set('showtable', $showtable);
412 $this->__set('printview', $printview);
413 $this->__set('url_query', $url_query);
414 $this->__set('editable', $editable);
415 $this->__set('is_browse_distinct', $is_browse_dist);
417 } // end of the 'setProperties()' function
421 * Defines the parts to display for a print view
423 * @param array $displayParts the parts to display
425 * @return array $displayParts the modified display parts
427 * @access private
430 private function _setDisplayPartsForPrintView($displayParts)
432 // set all elements to false!
433 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link
434 $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link
435 $displayParts['sort_lnk'] = (string) '0';
436 $displayParts['nav_bar'] = (string) '0';
437 $displayParts['bkm_form'] = (string) '0';
438 $displayParts['text_btn'] = (string) '0';
439 $displayParts['pview_lnk'] = (string) '0';
441 return $displayParts;
445 * Defines the parts to display for a SHOW statement
447 * @param array $displayParts the parts to display
449 * @return array $displayParts the modified display parts
451 * @access private
454 private function _setDisplayPartsForShow($displayParts)
456 preg_match(
457 '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
458 . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
459 . ')@i',
460 $this->__get('sql_query'), $which
463 $bIsProcessList = isset($which[1]);
464 if ($bIsProcessList) {
465 $str = ' ' . strtoupper($which[1]);
466 $bIsProcessList = $bIsProcessList
467 && strpos($str, 'PROCESSLIST') > 0;
470 if ($bIsProcessList) {
471 // no edit link
472 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
473 // "kill process" type edit link
474 $displayParts['del_lnk'] = self::KILL_PROCESS;
475 } else {
476 // Default case -> no links
477 // no edit link
478 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
479 // no delete link
480 $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
482 // Other settings
483 $displayParts['sort_lnk'] = (string) '0';
484 $displayParts['nav_bar'] = (string) '0';
485 $displayParts['bkm_form'] = (string) '1';
486 $displayParts['text_btn'] = (string) '1';
487 $displayParts['pview_lnk'] = (string) '1';
489 return $displayParts;
493 * Defines the parts to display for statements not related to data
495 * @param array $displayParts the parts to display
497 * @return array $displayParts the modified display parts
499 * @access private
502 private function _setDisplayPartsForNonData($displayParts)
504 // Statement is a "SELECT COUNT", a
505 // "CHECK/ANALYZE/REPAIR/OPTIMIZE/CHECKSUM", an "EXPLAIN" one or
506 // contains a "PROC ANALYSE" part
507 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link
508 $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link
509 $displayParts['sort_lnk'] = (string) '0';
510 $displayParts['nav_bar'] = (string) '0';
511 $displayParts['bkm_form'] = (string) '1';
513 if ($this->__get('is_maint')) {
514 $displayParts['text_btn'] = (string) '1';
515 } else {
516 $displayParts['text_btn'] = (string) '0';
518 $displayParts['pview_lnk'] = (string) '1';
520 return $displayParts;
524 * Defines the parts to display for other statements (probably SELECT)
526 * @param array $displayParts the parts to display
528 * @return array $displayParts the modified display parts
530 * @access private
533 private function _setDisplayPartsForSelect($displayParts)
535 // Other statements (ie "SELECT" ones) -> updates
536 // $displayParts['edit_lnk'], $displayParts['del_lnk'] and
537 // $displayParts['text_btn'] (keeps other default values)
539 $fields_meta = $this->__get('fields_meta');
540 $prev_table = '';
541 $displayParts['text_btn'] = (string) '1';
542 $number_of_columns = $this->__get('fields_cnt');
544 for ($i = 0; $i < $number_of_columns; $i++) {
546 $is_link = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
547 || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
548 || ($displayParts['sort_lnk'] != '0');
550 // Displays edit/delete/sort/insert links?
551 if ($is_link
552 && $prev_table != ''
553 && $fields_meta[$i]->table != ''
554 && $fields_meta[$i]->table != $prev_table
556 // don't display links
557 $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
558 $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
560 * @todo May be problematic with same field names
561 * in two joined table.
563 // $displayParts['sort_lnk'] = (string) '0';
564 if ($displayParts['text_btn'] == '1') {
565 break;
567 } // end if
569 // Always display print view link
570 $displayParts['pview_lnk'] = (string) '1';
571 if ($fields_meta[$i]->table != '') {
572 $prev_table = $fields_meta[$i]->table;
574 } // end for
575 return $displayParts;
579 * Defines the parts to display for the results of a SQL query
580 * and the total number of rows
582 * @param array $displayParts the parts to display (see a few
583 * lines above for explanations)
585 * @return array the first element is an array with explicit indexes
586 * for all the display elements
587 * the second element is the total number of rows returned
588 * by the SQL query without any programmatically appended
589 * LIMIT clause (just a copy of $unlim_num_rows if it exists,
590 * else computed inside this function)
593 * @access private
595 * @see getTable()
597 private function _setDisplayPartsAndTotal($displayParts)
599 $the_total = 0;
601 // 1. Following variables are needed for use in isset/empty or
602 // use with array indexes or safe use in foreach
603 $db = $this->__get('db');
604 $table = $this->__get('table');
605 $unlim_num_rows = $this->__get('unlim_num_rows');
606 $num_rows = $this->__get('num_rows');
607 $printview = $this->__get('printview');
609 // 2. Updates the display parts
610 if ($printview == '1') {
611 $displayParts = $this->_setDisplayPartsForPrintView($displayParts);
613 } elseif ($this->__get('is_count') || $this->__get('is_analyse')
614 || $this->__get('is_maint') || $this->__get('is_explain')
616 $displayParts = $this->_setDisplayPartsForNonData($displayParts);
618 } elseif ($this->__get('is_show')) {
619 $displayParts = $this->_setDisplayPartsForShow($displayParts);
621 } else {
622 $displayParts = $this->_setDisplayPartsForSelect($displayParts);
623 } // end if..elseif...else
625 // 3. Gets the total number of rows if it is unknown
626 if (isset($unlim_num_rows) && $unlim_num_rows != '') {
627 $the_total = $unlim_num_rows;
628 } elseif ((($displayParts['nav_bar'] == '1')
629 || ($displayParts['sort_lnk'] == '1'))
630 && (mb_strlen($db) && !empty($table))
632 $the_total = $GLOBALS['dbi']->getTable($db, $table)->countRecords();
635 // if for COUNT query, number of rows returned more than 1
636 // (may be being used GROUP BY)
637 if ($this->__get('is_count') && isset($num_rows) && $num_rows > 1) {
638 $displayParts['nav_bar'] = (string) '1';
639 $displayParts['sort_lnk'] = (string) '1';
641 // 4. If navigation bar or sorting fields names URLs should be
642 // displayed but there is only one row, change these settings to
643 // false
644 if ($displayParts['nav_bar'] == '1' || $displayParts['sort_lnk'] == '1') {
646 // - Do not display sort links if less than 2 rows.
647 // - For a VIEW we (probably) did not count the number of rows
648 // so don't test this number here, it would remove the possibility
649 // of sorting VIEW results.
650 $_table = new Table($table, $db);
651 if (isset($unlim_num_rows)
652 && ($unlim_num_rows < 2)
653 && ! $_table->isView()
655 $displayParts['sort_lnk'] = (string) '0';
657 } // end if (3)
659 return array($displayParts, $the_total);
661 } // end of the 'setDisplayPartsAndTotal()' function
665 * Return true if we are executing a query in the form of
666 * "SELECT * FROM <a table> ..."
668 * @param array $analyzed_sql_results analyzed sql results
670 * @return boolean
672 * @access private
674 * @see _getTableHeaders(), _getColumnParams()
676 private function _isSelect($analyzed_sql_results)
678 return ! ($this->__get('is_count')
679 || $this->__get('is_export')
680 || $this->__get('is_func')
681 || $this->__get('is_analyse'))
682 && !empty($analyzed_sql_results['select_from'])
683 && !empty($analyzed_sql_results['statement']->from)
684 && (count($analyzed_sql_results['statement']->from) == 1)
685 && !empty($analyzed_sql_results['statement']->from[0]->table);
690 * Get a navigation button
692 * @param string $caption iconic caption for button
693 * @param string $title text for button
694 * @param integer $pos position for next query
695 * @param string $html_sql_query query ready for display
696 * @param boolean $back whether 'begin' or 'previous'
697 * @param string $onsubmit optional onsubmit clause
698 * @param string $input_for_real_end optional hidden field for special treatment
699 * @param string $onclick optional onclick clause
701 * @return string html content
703 * @access private
705 * @see _getMoveBackwardButtonsForTableNavigation(),
706 * _getMoveForwardButtonsForTableNavigation()
708 private function _getTableNavigationButton(
709 $caption, $title, $pos, $html_sql_query, $back, $onsubmit = '',
710 $input_for_real_end = '', $onclick = ''
713 $caption_output = '';
714 if ($back) {
715 if (Util::showIcons('TableNavigationLinksMode')) {
716 $caption_output .= $caption;
718 if (Util::showText('TableNavigationLinksMode')) {
719 $caption_output .= '&nbsp;' . $title;
721 } else {
722 if (Util::showText('TableNavigationLinksMode')) {
723 $caption_output .= $title;
725 if (Util::showIcons('TableNavigationLinksMode')) {
726 $caption_output .= '&nbsp;' . $caption;
729 $title_output = ' title="' . $title . '"';
731 return '<td>'
732 . '<form action="sql.php" method="post" ' . $onsubmit . '>'
733 . PMA_URL_getHiddenInputs(
734 $this->__get('db'), $this->__get('table')
736 . '<input type="hidden" name="sql_query" value="'
737 . $html_sql_query . '" />'
738 . '<input type="hidden" name="pos" value="' . $pos . '" />'
739 . '<input type="hidden" name="is_browse_distinct" value="'
740 . $this->__get('is_browse_distinct') . '" />'
741 . '<input type="hidden" name="goto" value="' . $this->__get('goto')
742 . '" />'
743 . $input_for_real_end
744 . '<input type="submit" name="navig"'
745 . ' class="ajax" '
746 . 'value="' . $caption_output . '" ' . $title_output . $onclick . ' />'
747 . '</form>'
748 . '</td>';
750 } // end function _getTableNavigationButton()
754 * Possibly return a page selector for table navigation
756 * @param string $table_navigation_html the current navigation HTML
758 * @return array ($table_navigation_html, $nbTotalPage)
760 * @access private
763 private function _getHtmlPageSelector($table_navigation_html)
765 $pageNow = @floor(
766 $_SESSION['tmpval']['pos']
767 / $_SESSION['tmpval']['max_rows']
768 ) + 1;
770 $nbTotalPage = @ceil(
771 $this->__get('unlim_num_rows')
772 / $_SESSION['tmpval']['max_rows']
775 if ($nbTotalPage > 1) {
776 $table_navigation_html .= '<td>';
777 $_url_params = array(
778 'db' => $this->__get('db'),
779 'table' => $this->__get('table'),
780 'sql_query' => $this->__get('sql_query'),
781 'goto' => $this->__get('goto'),
782 'is_browse_distinct' => $this->__get('is_browse_distinct'),
785 //<form> to keep the form alignment of button < and <<
786 // and also to know what to execute when the selector changes
787 $table_navigation_html .= '<form action="sql.php'
788 . PMA_URL_getCommon($_url_params)
789 . '" method="post">';
791 $table_navigation_html .= Util::pageselector(
792 'pos',
793 $_SESSION['tmpval']['max_rows'],
794 $pageNow, $nbTotalPage, 200, 5, 5, 20, 10
797 $table_navigation_html .= '</form>'
798 . '</td>';
800 return array($table_navigation_html, $nbTotalPage);
804 * Get a navigation bar to browse among the results of a SQL query
806 * @param integer $pos_next the offset for the "next" page
807 * @param integer $pos_prev the offset for the "previous" page
808 * @param boolean $is_innodb whether its InnoDB or not
809 * @param string $sort_by_key_html the sort by key dialog
811 * @return string html content
813 * @access private
815 * @see _getTable()
817 private function _getTableNavigation(
818 $pos_next, $pos_prev, $is_innodb, $sort_by_key_html
821 $table_navigation_html = '';
823 // here, using htmlentities() would cause problems if the query
824 // contains accented characters
825 $html_sql_query = htmlspecialchars($this->__get('sql_query'));
827 // Navigation bar
828 $table_navigation_html
829 .= '<table class="navigation nospacing nopadding print_ignore">'
830 . '<tr>'
831 . '<td class="navigation_separator"></td>';
833 // Move to the beginning or to the previous page
834 if ($_SESSION['tmpval']['pos']
835 && ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS)
838 $table_navigation_html
839 .= $this->_getMoveBackwardButtonsForTableNavigation(
840 $html_sql_query, $pos_prev
843 } // end move back
845 $nbTotalPage = 1;
846 //page redirection
847 // (unless we are showing all records)
848 if ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS) {
849 list(
850 $table_navigation_html,
851 $nbTotalPage
852 ) = $this->_getHtmlPageSelector($table_navigation_html);
855 $showing_all = false;
856 if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
857 $showing_all = true;
860 // Move to the next page or to the last one
861 $endpos = $_SESSION['tmpval']['pos']
862 + $_SESSION['tmpval']['max_rows'];
864 if (($endpos < $this->__get('unlim_num_rows'))
865 && ($this->__get('num_rows') >= $_SESSION['tmpval']['max_rows'])
866 && ($_SESSION['tmpval']['max_rows'] != self::ALL_ROWS)
869 $table_navigation_html
870 .= $this->_getMoveForwardButtonsForTableNavigation(
871 $html_sql_query, $pos_next, $is_innodb
874 } // end move toward
876 // show separator if pagination happen
877 if ($nbTotalPage > 1) {
878 $table_navigation_html
879 .= '<td><div class="navigation_separator">|</div></td>';
882 // Display the "Show all" button if allowed
883 if ($GLOBALS['cfg']['ShowAll'] || ($this->__get('unlim_num_rows') <= 500) ) {
885 $table_navigation_html .= $this->_getShowAllCheckboxForTableNavigation(
886 $showing_all, $html_sql_query
889 $table_navigation_html
890 .= '<td><div class="navigation_separator">|</div></td>';
892 } // end show all
894 $table_navigation_html .= '<td>'
895 . '<div class="save_edited hide">'
896 . '<input type="submit" value="' . __('Save edited data') . '" />'
897 . '<div class="navigation_separator">|</div>'
898 . '</div>'
899 . '</td>'
900 . '<td>'
901 . '<div class="restore_column hide">'
902 . '<input type="submit" value="' . __('Restore column order') . '" />'
903 . '<div class="navigation_separator">|</div>'
904 . '</div>'
905 . '</td>';
907 // if displaying a VIEW, $unlim_num_rows could be zero because
908 // of $cfg['MaxExactCountViews']; in this case, avoid passing
909 // the 5th parameter to checkFormElementInRange()
910 // (this means we can't validate the upper limit
911 $table_navigation_html .= '<td class="navigation_goto">';
913 $table_navigation_html .= '<form action="sql.php" method="post" '
914 . 'onsubmit="return '
915 . '(checkFormElementInRange('
916 . 'this, '
917 . '\'session_max_rows\', '
918 . '\''
919 . str_replace('\'', '\\\'', __('%d is not valid row number.'))
920 . '\', '
921 . '1)'
922 . ' &amp;&amp; '
923 . 'checkFormElementInRange('
924 . 'this, '
925 . '\'pos\', '
926 . '\''
927 . str_replace('\'', '\\\'', __('%d is not valid row number.'))
928 . '\', '
929 . '0'
930 . (($this->__get('unlim_num_rows') > 0)
931 ? ', ' . ($this->__get('unlim_num_rows') - 1)
932 : ''
934 . ')'
935 . ')'
936 . '">';
938 $table_navigation_html .= PMA_URL_getHiddenInputs(
939 $this->__get('db'), $this->__get('table')
942 $table_navigation_html .= $this->_getAdditionalFieldsForTableNavigation(
943 $html_sql_query
946 $table_navigation_html .= '</form>'
947 . '</td>'
948 . '<td class="navigation_separator"></td>'
949 . '<td>'
950 . '<span>' . __('Filter rows') . ':</span>'
951 . '<input type="text" class="filter_rows"'
952 . ' placeholder="' . __('Search this table') . '"'
953 . ' data-for="' . $this->__get('unique_id') . '" />'
954 . '</td>';
956 $table_navigation_html .= '<td>' . $sort_by_key_html . '</td>';
958 $table_navigation_html .= '<td class="navigation_separator"></td>'
959 . '</tr>'
960 . '</table>';
962 return $table_navigation_html;
964 } // end of the '_getTableNavigation()' function
968 * Prepare move backward buttons - previous and first
970 * @param string $html_sql_query the sql encoded by html special characters
971 * @param integer $pos_prev the offset for the "previous" page
973 * @return string html content
975 * @access private
977 * @see _getTableNavigation()
979 private function _getMoveBackwardButtonsForTableNavigation(
980 $html_sql_query, $pos_prev
982 return $this->_getTableNavigationButton(
983 '&lt;&lt;', _pgettext('First page', 'Begin'), 0, $html_sql_query, true
985 . $this->_getTableNavigationButton(
986 '&lt;', _pgettext('Previous page', 'Previous'), $pos_prev,
987 $html_sql_query, true
989 } // end of the '_getMoveBackwardButtonsForTableNavigation()' function
993 * Prepare Show All checkbox for table navigation
995 * @param bool $showing_all whether all rows are shown currently
996 * @param string $html_sql_query the sql encoded by html special characters
998 * @return string html content
1000 * @access private
1002 * @see _getTableNavigation()
1004 private function _getShowAllCheckboxForTableNavigation(
1005 $showing_all, $html_sql_query
1007 return "\n"
1008 . '<td>'
1009 . '<form action="sql.php" method="post">'
1010 . PMA_URL_getHiddenInputs(
1011 $this->__get('db'), $this->__get('table')
1013 . '<input type="hidden" name="sql_query" value="'
1014 . $html_sql_query . '" />'
1015 . '<input type="hidden" name="pos" value="0" />'
1016 . '<input type="hidden" name="is_browse_distinct" value="'
1017 . $this->__get('is_browse_distinct') . '" />'
1018 . '<input type="hidden" name="session_max_rows" value="'
1019 . (! $showing_all ? 'all' : $GLOBALS['cfg']['MaxRows']) . '" />'
1020 . '<input type="hidden" name="goto" value="' . $this->__get('goto')
1021 . '" />'
1022 . '<input type="checkbox" name="navig"'
1023 . ' id="showAll_' . $this->__get('unique_id') . '" class="showAllRows"'
1024 . (! $showing_all ? '' : ' checked="checked"') . ' value="all" />'
1025 . '<label for="showAll_' . $this->__get('unique_id') . '">'
1026 . __('Show all') . '</label>'
1027 . '</form>'
1028 . '</td>';
1029 } // end of the '_getShowAllButtonForTableNavigation()' function
1033 * Prepare move forward buttons - next and last
1035 * @param string $html_sql_query the sql encoded by htmlspecialchars()
1036 * @param integer $pos_next the offset for the "next" page
1037 * @param boolean $is_innodb whether it's InnoDB or not
1039 * @return string $buttons_html html content
1041 * @access private
1043 * @see _getTableNavigation()
1045 private function _getMoveForwardButtonsForTableNavigation(
1046 $html_sql_query, $pos_next, $is_innodb
1049 // display the Next button
1050 $buttons_html = $this->_getTableNavigationButton(
1051 '&gt;',
1052 _pgettext('Next page', 'Next'),
1053 $pos_next,
1054 $html_sql_query,
1055 false
1058 // prepare some options for the End button
1059 if ($is_innodb
1060 && $this->__get('unlim_num_rows') > $GLOBALS['cfg']['MaxExactCount']
1062 $input_for_real_end = '<input id="real_end_input" type="hidden" '
1063 . 'name="find_real_end" value="1" />';
1064 // no backquote around this message
1065 $onclick = '';
1066 } else {
1067 $input_for_real_end = $onclick = '';
1070 $maxRows = $_SESSION['tmpval']['max_rows'];
1071 $onsubmit = 'onsubmit="return '
1072 . ($_SESSION['tmpval']['pos']
1073 + $maxRows
1074 < $this->__get('unlim_num_rows')
1075 && $this->__get('num_rows') >= $maxRows)
1076 ? 'true'
1077 : 'false' . '"';
1079 // display the End button
1080 $buttons_html .= $this->_getTableNavigationButton(
1081 '&gt;&gt;',
1082 _pgettext('Last page', 'End'),
1083 @((ceil(
1084 $this->__get('unlim_num_rows')
1085 / $_SESSION['tmpval']['max_rows']
1086 )- 1) * $maxRows),
1087 $html_sql_query, false, $onsubmit, $input_for_real_end, $onclick
1090 return $buttons_html;
1092 } // end of the '_getMoveForwardButtonsForTableNavigation()' function
1096 * Prepare fields for table navigation
1097 * Number of rows
1099 * @param string $html_sql_query the sql encoded by htmlspecialchars()
1101 * @return string $additional_fields_html html content
1103 * @access private
1105 * @see _getTableNavigation()
1107 private function _getAdditionalFieldsForTableNavigation(
1108 $html_sql_query
1111 $additional_fields_html = '';
1113 $additional_fields_html .= '<input type="hidden" name="sql_query" '
1114 . 'value="' . $html_sql_query . '" />'
1115 . '<input type="hidden" name="goto" value="' . $this->__get('goto')
1116 . '" />'
1117 . '<input type="hidden" name="pos" size="3" value="'
1118 // Do not change the position when changing the number of rows
1119 . $_SESSION['tmpval']['pos'] . '" />'
1120 . '<input type="hidden" name="is_browse_distinct" value="'
1121 . $this->__get('is_browse_distinct') . '" />' ;
1123 $numberOfRowsPlaceholder = null;
1124 if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
1125 $numberOfRowsPlaceholder = __('All');
1128 $numberOfRowsChoices = array(
1129 '25' => 25,
1130 '50' => 50,
1131 '100' => 100,
1132 '250' => 250,
1133 '500' => 500
1135 $additional_fields_html .= __('Number of rows:') . ' ';
1136 $additional_fields_html .= Util::getDropdown(
1137 'session_max_rows', $numberOfRowsChoices,
1138 $_SESSION['tmpval']['max_rows'], '',
1139 'autosubmit', $numberOfRowsPlaceholder
1142 return $additional_fields_html;
1144 } // end of the '_getAdditionalFieldsForTableNavigation()' function
1148 * Get the headers of the results table, for all of the columns
1150 * @param array $displayParts which elements to display
1151 * @param array $analyzed_sql_results analyzed sql results
1152 * @param array $sort_expression sort expression
1153 * @param string $sort_expression_nodirection sort expression
1154 * without direction
1155 * @param string $sort_direction sort direction
1156 * @param boolean $is_limited_display with limited operations
1157 * or not
1158 * @param string $unsorted_sql_query query without the sort part
1160 * @return string html content
1162 * @access private
1164 * @see getTableHeaders()
1166 private function _getTableHeadersForColumns(
1167 $displayParts, $analyzed_sql_results, $sort_expression,
1168 $sort_expression_nodirection, $sort_direction, $is_limited_display,
1169 $unsorted_sql_query
1171 $html = '';
1173 // required to generate sort links that will remember whether the
1174 // "Show all" button has been clicked
1175 $sql_md5 = md5($this->__get('sql_query'));
1176 $session_max_rows = $is_limited_display
1178 : $_SESSION['tmpval']['query'][$sql_md5]['max_rows'];
1180 // Following variable are needed for use in isset/empty or
1181 // use with array indexes/safe use in the for loop
1182 $highlight_columns = $this->__get('highlight_columns');
1183 $fields_meta = $this->__get('fields_meta');
1185 // Prepare Display column comments if enabled
1186 // ($GLOBALS['cfg']['ShowBrowseComments']).
1187 $comments_map = $this->_getTableCommentsArray($analyzed_sql_results);
1189 list($col_order, $col_visib) = $this->_getColumnParams(
1190 $analyzed_sql_results
1193 // optimize: avoid calling a method on each iteration
1194 $number_of_columns = $this->__get('fields_cnt');
1196 for ($j = 0; $j < $number_of_columns; $j++) {
1198 // assign $i with the appropriate column order
1199 $i = $col_order ? $col_order[$j] : $j;
1201 // See if this column should get highlight because it's used in the
1202 // where-query.
1203 $name = $fields_meta[$i]->name;
1204 $condition_field = (isset($highlight_columns[$name])
1205 || isset($highlight_columns[Util::backquote($name)]))
1206 ? true
1207 : false;
1209 // Prepare comment-HTML-wrappers for each row, if defined/enabled.
1210 $comments = $this->_getCommentForRow($comments_map, $fields_meta[$i]);
1211 $display_params = $this->__get('display_params');
1213 if (($displayParts['sort_lnk'] == '1') && ! $is_limited_display) {
1215 list($order_link, $sorted_header_html)
1216 = $this->_getOrderLinkAndSortedHeaderHtml(
1217 $fields_meta[$i], $sort_expression,
1218 $sort_expression_nodirection, $i, $unsorted_sql_query,
1219 $session_max_rows, $comments,
1220 $sort_direction, $col_visib,
1221 $col_visib[$j]
1224 $html .= $sorted_header_html;
1226 $display_params['desc'][] = ' <th '
1227 . 'class="draggable'
1228 . ($condition_field ? ' condition' : '')
1229 . '" data-column="' . htmlspecialchars($fields_meta[$i]->name)
1230 . '">' . "\n" . $order_link . $comments . ' </th>' . "\n";
1231 } else {
1232 // Results can't be sorted
1233 $html
1234 .= $this->_getDraggableClassForNonSortableColumns(
1235 $col_visib, $col_visib[$j], $condition_field,
1236 $fields_meta[$i], $comments
1239 $display_params['desc'][] = ' <th '
1240 . 'class="draggable'
1241 . ($condition_field ? ' condition"' : '')
1242 . '" data-column="' . htmlspecialchars($fields_meta[$i]->name)
1243 . '">' . ' '
1244 . htmlspecialchars($fields_meta[$i]->name)
1245 . $comments . ' </th>';
1246 } // end else
1248 $this->__set('display_params', $display_params);
1250 } // end for
1251 return $html;
1255 * Get the headers of the results table
1257 * @param array &$displayParts which elements to display
1258 * @param array $analyzed_sql_results analyzed sql results
1259 * @param string $unsorted_sql_query the unsorted sql query
1260 * @param array $sort_expression sort expression
1261 * @param string $sort_expression_nodirection sort expression
1262 * without direction
1263 * @param string $sort_direction sort direction
1264 * @param boolean $is_limited_display with limited operations
1265 * or not
1267 * @return string html content
1269 * @access private
1271 * @see getTable()
1273 private function _getTableHeaders(
1274 &$displayParts, $analyzed_sql_results, $unsorted_sql_query,
1275 $sort_expression = array(), $sort_expression_nodirection = '',
1276 $sort_direction = '', $is_limited_display = false
1279 $table_headers_html = '';
1280 // Needed for use in isset/empty or
1281 // use with array indexes/safe use in foreach
1282 $printview = $this->__get('printview');
1283 $display_params = $this->__get('display_params');
1285 // Output data needed for grid editing
1286 $table_headers_html .= '<input class="save_cells_at_once" type="hidden"'
1287 . ' value="' . $GLOBALS['cfg']['SaveCellsAtOnce'] . '" />'
1288 . '<div class="common_hidden_inputs">'
1289 . PMA_URL_getHiddenInputs(
1290 $this->__get('db'), $this->__get('table')
1292 . '</div>';
1294 // Output data needed for column reordering and show/hide column
1295 if ($this->_isSelect($analyzed_sql_results)) {
1296 $table_headers_html .= $this->_getDataForResettingColumnOrder();
1299 $display_params['emptypre'] = 0;
1300 $display_params['emptyafter'] = 0;
1301 $display_params['textbtn'] = '';
1302 $full_or_partial_text_link = null;
1304 $this->__set('display_params', $display_params);
1306 // Display options (if we are not in print view)
1307 if (! (isset($printview) && ($printview == '1')) && ! $is_limited_display) {
1309 $table_headers_html .= $this->_getOptionsBlock();
1311 // prepare full/partial text button or link
1312 $full_or_partial_text_link = $this->_getFullOrPartialTextButtonOrLink();
1315 // Start of form for multi-rows edit/delete/export
1316 $table_headers_html .= $this->_getFormForMultiRowOperations(
1317 $displayParts['del_lnk']
1320 // 1. Set $colspan and generate html with full/partial
1321 // text button or link
1322 list($colspan, $button_html)
1323 = $this->_getFieldVisibilityParams(
1324 $displayParts, $full_or_partial_text_link
1327 $table_headers_html .= $button_html;
1329 // 2. Displays the fields' name
1330 // 2.0 If sorting links should be used, checks if the query is a "JOIN"
1331 // statement (see 2.1.3)
1333 // See if we have to highlight any header fields of a WHERE query.
1334 // Uses SQL-Parser results.
1335 $this->_setHighlightedColumnGlobalField($analyzed_sql_results);
1337 // Get the headers for all of the columns
1338 $table_headers_html .= $this->_getTableHeadersForColumns(
1339 $displayParts, $analyzed_sql_results, $sort_expression,
1340 $sort_expression_nodirection, $sort_direction,
1341 $is_limited_display, $unsorted_sql_query
1344 // Display column at rightside - checkboxes or empty column
1345 if (! $printview) {
1346 $table_headers_html .= $this->_getColumnAtRightSide(
1347 $displayParts, $full_or_partial_text_link, $colspan
1350 $table_headers_html .= '</tr>' . '</thead>';
1352 return $table_headers_html;
1354 } // end of the '_getTableHeaders()' function
1358 * Prepare unsorted sql query and sort by key drop down
1360 * @param array $analyzed_sql_results analyzed sql results
1361 * @param string $sort_expression sort expression
1363 * @return array two element array - $unsorted_sql_query, $drop_down_html
1365 * @access private
1367 * @see _getTableHeaders()
1369 private function _getUnsortedSqlAndSortByKeyDropDown(
1370 $analyzed_sql_results, $sort_expression
1372 $drop_down_html = '';
1374 $unsorted_sql_query = Query::replaceClause(
1375 $analyzed_sql_results['statement'],
1376 $analyzed_sql_results['parser']->list,
1377 'ORDER BY',
1381 // Data is sorted by indexes only if it there is only one table.
1382 if ($this->_isSelect($analyzed_sql_results)) {
1383 // grab indexes data:
1384 $indexes = Index::getFromTable(
1385 $this->__get('table'),
1386 $this->__get('db')
1389 // do we have any index?
1390 if (! empty($indexes)) {
1391 $drop_down_html = $this->_getSortByKeyDropDown(
1392 $indexes, $sort_expression,
1393 $unsorted_sql_query
1398 return array($unsorted_sql_query, $drop_down_html);
1400 } // end of the '_getUnsortedSqlAndSortByKeyDropDown()' function
1403 * Prepare sort by key dropdown - html code segment
1405 * @param Index[] $indexes the indexes of the table for sort criteria
1406 * @param string $sort_expression the sort expression
1407 * @param string $unsorted_sql_query the unsorted sql query
1409 * @return string $drop_down_html html content
1411 * @access private
1413 * @see _getTableHeaders()
1415 private function _getSortByKeyDropDown(
1416 $indexes, $sort_expression, $unsorted_sql_query
1419 $drop_down_html = '';
1421 $drop_down_html .= '<form action="sql.php" method="post" ' .
1422 'class="print_ignore">' . "\n"
1423 . PMA_URL_getHiddenInputs(
1424 $this->__get('db'), $this->__get('table')
1426 // to avoid calling PMA_handleSortOrder() later
1427 . PMA_getHiddenFields(array('sort_by_key' => '1'))
1428 . __('Sort by key')
1429 . ': <select name="sql_query" class="autosubmit">' . "\n";
1431 $used_index = false;
1432 $local_order = (isset($sort_expression) ? $sort_expression : '');
1434 foreach ($indexes as $index) {
1436 $asc_sort = '`'
1437 . implode('` ASC, `', array_keys($index->getColumns()))
1438 . '` ASC';
1440 $desc_sort = '`'
1441 . implode('` DESC, `', array_keys($index->getColumns()))
1442 . '` DESC';
1444 $used_index = $used_index
1445 || ($local_order == $asc_sort)
1446 || ($local_order == $desc_sort);
1448 if (preg_match(
1449 '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|'
1450 . 'FOR UPDATE|LOCK IN SHARE MODE))@is',
1451 $unsorted_sql_query, $my_reg
1452 )) {
1453 $unsorted_sql_query_first_part = $my_reg[1];
1454 $unsorted_sql_query_second_part = $my_reg[2];
1455 } else {
1456 $unsorted_sql_query_first_part = $unsorted_sql_query;
1457 $unsorted_sql_query_second_part = '';
1460 $drop_down_html .= '<option value="'
1461 . htmlspecialchars(
1462 $unsorted_sql_query_first_part . "\n"
1463 . ' ORDER BY ' . $asc_sort
1464 . $unsorted_sql_query_second_part
1466 . '"' . ($local_order == $asc_sort
1467 ? ' selected="selected"'
1468 : '')
1469 . '>' . htmlspecialchars($index->getName()) . ' (ASC)</option>';
1471 $drop_down_html .= '<option value="'
1472 . htmlspecialchars(
1473 $unsorted_sql_query_first_part . "\n"
1474 . ' ORDER BY ' . $desc_sort
1475 . $unsorted_sql_query_second_part
1477 . '"' . ($local_order == $desc_sort
1478 ? ' selected="selected"'
1479 : '')
1480 . '>' . htmlspecialchars($index->getName()) . ' (DESC)</option>';
1483 $drop_down_html .= '<option value="' . htmlspecialchars($unsorted_sql_query)
1484 . '"' . ($used_index ? '' : ' selected="selected"') . '>' . __('None')
1485 . '</option>'
1486 . '</select>' . "\n"
1487 . '</form>' . "\n";
1489 return $drop_down_html;
1491 } // end of the '_getSortByKeyDropDown()' function
1495 * Set column span, row span and prepare html with full/partial
1496 * text button or link
1498 * @param array &$displayParts which elements to display
1499 * @param string $full_or_partial_text_link full/partial link or text button
1501 * @return array 2 element array - $colspan, $button_html
1503 * @access private
1505 * @see _getTableHeaders()
1507 private function _getFieldVisibilityParams(
1508 &$displayParts, $full_or_partial_text_link
1511 $button_html = '';
1512 $display_params = $this->__get('display_params');
1514 // 1. Displays the full/partial text button (part 1)...
1515 $button_html .= '<thead><tr>' . "\n";
1517 $colspan = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
1518 && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE))
1519 ? ' colspan="4"'
1520 : '';
1522 // ... before the result table
1523 if ((($displayParts['edit_lnk'] == self::NO_EDIT_OR_DELETE)
1524 && ($displayParts['del_lnk'] == self::NO_EDIT_OR_DELETE))
1525 && ($displayParts['text_btn'] == '1')
1528 $display_params['emptypre']
1529 = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
1530 && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0;
1532 } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
1533 || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
1534 && ($displayParts['text_btn'] == '1')
1536 // ... at the left column of the result table header if possible
1537 // and required
1539 $display_params['emptypre']
1540 = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
1541 && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0;
1543 $button_html .= '<th class="column_action print_ignore" ' . $colspan
1544 . '>' . $full_or_partial_text_link . '</th>';
1546 } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
1547 || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
1548 && (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
1549 || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE))
1551 // ... elseif no button, displays empty(ies) col(s) if required
1553 $display_params['emptypre']
1554 = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
1555 && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0;
1557 $button_html .= '<td ' . $colspan . '></td>';
1559 } elseif (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE)) {
1560 // ... elseif display an empty column if the actions links are
1561 // disabled to match the rest of the table
1562 $button_html .= '<th class="column_action"></th>';
1565 $this->__set('display_params', $display_params);
1567 return array($colspan, $button_html);
1569 } // end of the '_getFieldVisibilityParams()' function
1573 * Get table comments as array
1575 * @param array $analyzed_sql_results analyzed sql results
1577 * @return array $comments_map table comments
1579 * @access private
1581 * @see _getTableHeaders()
1583 private function _getTableCommentsArray($analyzed_sql_results)
1585 if ((!$GLOBALS['cfg']['ShowBrowseComments'])
1586 || (empty($analyzed_sql_results['statement']->from))
1588 return array();
1591 $ret = array();
1592 foreach ($analyzed_sql_results['statement']->from as $field) {
1593 if (empty($field->table)) {
1594 continue;
1596 $ret[$field->table] = PMA_getComments(
1597 empty($field->database) ? $this->__get('db') : $field->database,
1598 $field->table
1602 return $ret;
1604 } // end of the '_getTableCommentsArray()' function
1608 * Set global array for store highlighted header fields
1610 * @param array $analyzed_sql_results analyzed sql results
1612 * @return void
1614 * @access private
1616 * @see _getTableHeaders()
1618 private function _setHighlightedColumnGlobalField($analyzed_sql_results)
1620 $highlight_columns = array();
1622 if (!empty($analyzed_sql_results['statement']->where)) {
1623 foreach ($analyzed_sql_results['statement']->where as $expr) {
1624 foreach ($expr->identifiers as $identifier) {
1625 $highlight_columns[$identifier] = 'true';
1630 $this->__set('highlight_columns', $highlight_columns);
1632 } // end of the '_setHighlightedColumnGlobalField()' function
1636 * Prepare data for column restoring and show/hide
1638 * @return string $data_html html content
1640 * @access private
1642 * @see _getTableHeaders()
1644 private function _getDataForResettingColumnOrder()
1647 $data_html = '';
1649 // generate the column order, if it is set
1650 $pmatable = new Table($this->__get('table'), $this->__get('db'));
1651 $col_order = $pmatable->getUiProp(Table::PROP_COLUMN_ORDER);
1653 if ($col_order) {
1654 $data_html .= '<input class="col_order" type="hidden" value="'
1655 . implode(',', $col_order) . '" />';
1658 $col_visib = $pmatable->getUiProp(Table::PROP_COLUMN_VISIB);
1660 if ($col_visib) {
1661 $data_html .= '<input class="col_visib" type="hidden" value="'
1662 . implode(',', $col_visib) . '" />';
1665 // generate table create time
1666 $table = new Table($this->__get('table'), $this->__get('db'));
1667 if (! $table->isView()) {
1668 $data_html .= '<input class="table_create_time" type="hidden" value="'
1669 . $GLOBALS['dbi']->getTable(
1670 $this->__get('db'), $this->__get('table')
1671 )->getStatusInfo('Create_time')
1672 . '" />';
1675 return $data_html;
1677 } // end of the '_getDataForResettingColumnOrder()' function
1681 * Prepare option fields block
1683 * @return string $options_html html content
1685 * @access private
1687 * @see _getTableHeaders()
1689 private function _getOptionsBlock()
1692 $options_html = '';
1694 $options_html .= '<form method="post" action="sql.php" '
1695 . 'name="displayOptionsForm"';
1697 $options_html .= ' class="ajax print_ignore" ';
1699 $options_html .= '>';
1700 $url_params = array(
1701 'db' => $this->__get('db'),
1702 'table' => $this->__get('table'),
1703 'sql_query' => $this->__get('sql_query'),
1704 'goto' => $this->__get('goto'),
1705 'display_options_form' => 1
1708 $options_html .= PMA_URL_getHiddenInputs($url_params)
1709 . '<br />'
1710 . Util::getDivForSliderEffect(
1711 '', __('Options')
1713 . '<fieldset>';
1715 $options_html .= '<div class="formelement">';
1716 $choices = array(
1717 'P' => __('Partial texts'),
1718 'F' => __('Full texts')
1721 // pftext means "partial or full texts" (done to reduce line lengths)
1722 $options_html .= Util::getRadioFields(
1723 'pftext', $choices,
1724 $_SESSION['tmpval']['pftext'],
1725 true, true, '', 'pftext_' . $this->__get('unique_id')
1727 . '</div>';
1729 if ($GLOBALS['cfgRelation']['relwork']
1730 && $GLOBALS['cfgRelation']['displaywork']
1732 $options_html .= '<div class="formelement">';
1733 $choices = array(
1734 'K' => __('Relational key'),
1735 'D' => __('Display column for relations')
1738 $options_html .= Util::getRadioFields(
1739 'relational_display', $choices,
1740 $_SESSION['tmpval']['relational_display'],
1741 true, true, '', 'relational_display_' . $this->__get('unique_id')
1743 . '</div>';
1746 $options_html .= '<div class="formelement">'
1747 . Util::getCheckbox(
1748 'display_binary', __('Show binary contents'),
1749 ! empty($_SESSION['tmpval']['display_binary']), false,
1750 'display_binary_' . $this->__get('unique_id')
1752 . '<br />'
1753 . Util::getCheckbox(
1754 'display_blob', __('Show BLOB contents'),
1755 ! empty($_SESSION['tmpval']['display_blob']), false,
1756 'display_blob_' . $this->__get('unique_id')
1758 . '</div>';
1760 // I would have preferred to name this "display_transformation".
1761 // This is the only way I found to be able to keep this setting sticky
1762 // per SQL query, and at the same time have a default that displays
1763 // the transformations.
1764 $options_html .= '<div class="formelement">'
1765 . Util::getCheckbox(
1766 'hide_transformation', __('Hide browser transformation'),
1767 ! empty($_SESSION['tmpval']['hide_transformation']), false,
1768 'hide_transformation_' . $this->__get('unique_id')
1770 . '</div>';
1772 $options_html .= '<div class="formelement">';
1773 $choices = array(
1774 'GEOM' => __('Geometry'),
1775 'WKT' => __('Well Known Text'),
1776 'WKB' => __('Well Known Binary')
1779 $options_html .= Util::getRadioFields(
1780 'geoOption', $choices,
1781 $_SESSION['tmpval']['geoOption'],
1782 true, true, '', 'geoOption_' . $this->__get('unique_id')
1784 $options_html .= '</div>';
1786 $options_html .= '<div class="clearfloat"></div>'
1787 . '</fieldset>';
1789 $options_html .= '<fieldset class="tblFooters">'
1790 . '<input type="submit" value="' . __('Go') . '" />'
1791 . '</fieldset>'
1792 . '</div>'
1793 . '</form>';
1795 return $options_html;
1797 } // end of the '_getOptionsBlock()' function
1801 * Get full/partial text button or link
1803 * @return string html content
1805 * @access private
1807 * @see _getTableHeaders()
1809 private function _getFullOrPartialTextButtonOrLink()
1812 $url_params_full_text = array(
1813 'db' => $this->__get('db'),
1814 'table' => $this->__get('table'),
1815 'sql_query' => $this->__get('sql_query'),
1816 'goto' => $this->__get('goto'),
1817 'full_text_button' => 1
1820 if ($_SESSION['tmpval']['pftext'] == self::DISPLAY_FULL_TEXT) {
1821 // currently in fulltext mode so show the opposite link
1822 $tmp_image_file = $this->__get('pma_theme_image') . 's_partialtext.png';
1823 $tmp_txt = __('Partial texts');
1824 $url_params_full_text['pftext'] = self::DISPLAY_PARTIAL_TEXT;
1825 } else {
1826 $tmp_image_file = $this->__get('pma_theme_image') . 's_fulltext.png';
1827 $tmp_txt = __('Full texts');
1828 $url_params_full_text['pftext'] = self::DISPLAY_FULL_TEXT;
1831 $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="'
1832 . $tmp_txt . '" title="' . $tmp_txt . '" />';
1833 $tmp_url = 'sql.php' . PMA_URL_getCommon($url_params_full_text);
1835 return Util::linkOrButton(
1836 $tmp_url, $tmp_image, array(), false
1839 } // end of the '_getFullOrPartialTextButtonOrLink()' function
1843 * Prepare html form for multi row operations
1845 * @param string $del_lnk the delete link of current row
1847 * @return string $form_html html content
1849 * @access private
1851 * @see _getTableHeaders()
1853 private function _getFormForMultiRowOperations($del_lnk)
1856 $form_html = '';
1858 if (($del_lnk == self::DELETE_ROW) || ($del_lnk == self::KILL_PROCESS)) {
1860 $form_html .= '<form method="post" action="tbl_row_action.php" '
1861 . 'name="resultsForm"'
1862 . ' id="resultsForm_' . $this->__get('unique_id') . '"';
1864 $form_html .= ' class="ajax" ';
1866 $form_html .= '>'
1867 . PMA_URL_getHiddenInputs(
1868 $this->__get('db'), $this->__get('table'), 1
1870 . '<input type="hidden" name="goto" value="sql.php" />';
1873 $form_html .= '<table class="table_results data ajax"';
1874 $form_html .= ' data-uniqueId="' . $this->__get('unique_id') . '"';
1875 $form_html .= '>';
1877 return $form_html;
1879 } // end of the '_getFormForMultiRowOperations()' function
1883 * Get comment for row
1885 * @param array $comments_map comments array
1886 * @param array $fields_meta set of field properties
1888 * @return string $comment html content
1890 * @access private
1892 * @see _getTableHeaders()
1894 private function _getCommentForRow($comments_map, $fields_meta)
1896 $comments = '';
1897 if (isset($comments_map[$fields_meta->table])
1898 && isset($comments_map[$fields_meta->table][$fields_meta->name])
1900 $sanitized_comments = htmlspecialchars(
1901 $comments_map[$fields_meta->table][$fields_meta->name]
1904 $comments = '<span class="tblcomment" title="'
1905 . $sanitized_comments . '">';
1906 $limitChars = $GLOBALS['cfg']['LimitChars'];
1907 if (mb_strlen($sanitized_comments) > $limitChars) {
1908 $sanitized_comments = mb_substr(
1909 $sanitized_comments, 0, $limitChars
1910 ) . '…';
1912 $comments .= $sanitized_comments;
1913 $comments .= '</span>';
1915 return $comments;
1916 } // end of the '_getCommentForRow()' function
1920 * Prepare parameters and html for sorted table header fields
1922 * @param array $fields_meta set of field properties
1923 * @param array $sort_expression sort expression
1924 * @param string $sort_expression_nodirection sort expression without direction
1925 * @param integer $column_index the index of the column
1926 * @param string $unsorted_sql_query the unsorted sql query
1927 * @param integer $session_max_rows maximum rows resulted by sql
1928 * @param string $comments comment for row
1929 * @param string $sort_direction sort direction
1930 * @param boolean $col_visib column is visible(false)
1931 * array column isn't visible(string array)
1932 * @param string $col_visib_j element of $col_visib array
1934 * @return array 2 element array - $order_link, $sorted_header_html
1936 * @access private
1938 * @see _getTableHeaders()
1940 private function _getOrderLinkAndSortedHeaderHtml(
1941 $fields_meta, $sort_expression, $sort_expression_nodirection,
1942 $column_index, $unsorted_sql_query, $session_max_rows,
1943 $comments, $sort_direction, $col_visib, $col_visib_j
1946 $sorted_header_html = '';
1948 // Checks if the table name is required; it's the case
1949 // for a query with a "JOIN" statement and if the column
1950 // isn't aliased, or in queries like
1951 // SELECT `1`.`master_field` , `2`.`master_field`
1952 // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2`
1954 $sort_tbl = (isset($fields_meta->table)
1955 && mb_strlen($fields_meta->table)
1956 && $fields_meta->orgname == $fields_meta->name)
1957 ? Util::backquote(
1958 $fields_meta->table
1959 ) . '.'
1960 : '';
1962 $name_to_use_in_sort = $fields_meta->name;
1964 // Generates the orderby clause part of the query which is part
1965 // of URL
1966 list($single_sort_order, $multi_sort_order, $order_img)
1967 = $this->_getSingleAndMultiSortUrls(
1968 $sort_expression, $sort_expression_nodirection, $sort_tbl,
1969 $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index
1972 if (preg_match(
1973 '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|'
1974 . 'LOCK IN SHARE MODE))@is',
1975 $unsorted_sql_query, $regs3
1976 )) {
1977 $single_sorted_sql_query = $regs3[1] . $single_sort_order . $regs3[2];
1978 $multi_sorted_sql_query = $regs3[1] . $multi_sort_order . $regs3[2];
1979 } else {
1980 $single_sorted_sql_query = $unsorted_sql_query . $single_sort_order;
1981 $multi_sorted_sql_query = $unsorted_sql_query . $multi_sort_order;
1984 $_single_url_params = array(
1985 'db' => $this->__get('db'),
1986 'table' => $this->__get('table'),
1987 'sql_query' => $single_sorted_sql_query,
1988 'session_max_rows' => $session_max_rows,
1989 'is_browse_distinct' => $this->__get('is_browse_distinct'),
1992 $_multi_url_params = array(
1993 'db' => $this->__get('db'),
1994 'table' => $this->__get('table'),
1995 'sql_query' => $multi_sorted_sql_query,
1996 'session_max_rows' => $session_max_rows,
1997 'is_browse_distinct' => $this->__get('is_browse_distinct'),
1999 $single_order_url = 'sql.php' . PMA_URL_getCommon($_single_url_params);
2000 $multi_order_url = 'sql.php' . PMA_URL_getCommon($_multi_url_params);
2002 // Displays the sorting URL
2003 // enable sort order swapping for image
2004 $order_link = $this->_getSortOrderLink(
2005 $order_img, $column_index,
2006 $fields_meta, $single_order_url, $multi_order_url
2009 $sorted_header_html .= $this->_getDraggableClassForSortableColumns(
2010 $col_visib, $col_visib_j,
2011 $fields_meta, $order_link, $comments
2014 return array($order_link, $sorted_header_html);
2016 } // end of the '_getOrderLinkAndSortedHeaderHtml()' function
2019 * Prepare parameters and html for sorted table header fields
2021 * @param array $sort_expression sort expression
2022 * @param string $sort_expression_nodirection sort expression without direction
2023 * @param string $sort_tbl The name of the table to which
2024 * the current column belongs to
2025 * @param string $name_to_use_in_sort The current column under
2026 * consideration
2027 * @param string $sort_direction sort direction
2028 * @param array $fields_meta set of field properties
2029 * @param integer $column_index The index number to current column
2031 * @return array 3 element array - $single_sort_order, $sort_order, $order_img
2033 * @access private
2035 * @see _getOrderLinkAndSortedHeaderHtml()
2037 private function _getSingleAndMultiSortUrls(
2038 $sort_expression, $sort_expression_nodirection, $sort_tbl,
2039 $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index
2041 $sort_order = "";
2042 // Check if the current column is in the order by clause
2043 $is_in_sort = $this->_isInSorted(
2044 $sort_expression, $sort_expression_nodirection,
2045 $sort_tbl, $name_to_use_in_sort
2047 $current_name = $name_to_use_in_sort;
2048 if ($sort_expression_nodirection[0] == '' || !$is_in_sort) {
2049 $special_index = $sort_expression_nodirection[0] == ''
2051 : count($sort_expression_nodirection);
2052 $sort_expression_nodirection[$special_index]
2053 = Util::backquote(
2054 $current_name
2056 $sort_direction[$special_index] = (preg_match(
2057 '@time|date@i',
2058 $fields_meta->type
2059 )) ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR;
2063 $sort_expression_nodirection = array_filter($sort_expression_nodirection);
2064 $single_sort_order = null;
2065 foreach ($sort_expression_nodirection as $index=>$expression) {
2066 // check if this is the first clause,
2067 // if it is then we have to add "order by"
2068 $is_first_clause = ($index == 0);
2069 $name_to_use_in_sort = $expression;
2070 $sort_tbl_new = $sort_tbl;
2071 // Test to detect if the column name is a standard name
2072 // Standard name has the table name prefixed to the column name
2073 if (mb_strpos($name_to_use_in_sort, '.') !== false) {
2074 $matches = explode('.', $name_to_use_in_sort);
2075 // Matches[0] has the table name
2076 // Matches[1] has the column name
2077 $name_to_use_in_sort = $matches[1];
2078 $sort_tbl_new = $matches[0];
2081 // $name_to_use_in_sort might contain a space due to
2082 // formatting of function expressions like "COUNT(name )"
2083 // so we remove the space in this situation
2084 $name_to_use_in_sort = str_replace(' )', ')', $name_to_use_in_sort);
2085 $name_to_use_in_sort = str_replace('``', '`', $name_to_use_in_sort);
2086 $name_to_use_in_sort = trim($name_to_use_in_sort, '`');
2088 // If this the first column name in the order by clause add
2089 // order by clause to the column name
2090 $query_head = $is_first_clause ? "\nORDER BY " : "";
2091 // Again a check to see if the given column is a aggregate column
2092 if (mb_strpos($name_to_use_in_sort, '(') !== false) {
2093 $sort_order .= $query_head . $name_to_use_in_sort . ' ' ;
2094 } else {
2095 if (mb_strlen($sort_tbl_new) > 0) {
2096 $sort_tbl_new .= ".";
2098 $sort_order .= $query_head . $sort_tbl_new
2099 . Util::backquote(
2100 $name_to_use_in_sort
2101 ) . ' ' ;
2104 // For a special case where the code generates two dots between
2105 // column name and table name.
2106 $sort_order = preg_replace("/\.\./", ".", $sort_order);
2107 // Incase this is the current column save $single_sort_order
2108 if ($current_name == $name_to_use_in_sort) {
2109 if (mb_strpos($current_name, '(') !== false) {
2110 $single_sort_order = "\n" . 'ORDER BY ' . $current_name . ' ';
2111 } else {
2112 $single_sort_order = "\n" . 'ORDER BY ' . $sort_tbl
2113 . Util::backquote(
2114 $current_name
2115 ) . ' ';
2117 if ($is_in_sort) {
2118 list($single_sort_order, $order_img)
2119 = $this->_getSortingUrlParams(
2120 $sort_direction, $single_sort_order,
2121 $column_index, $index
2123 } else {
2124 $single_sort_order .= strtoupper($sort_direction[$index]);
2127 if ($current_name == $name_to_use_in_sort && $is_in_sort) {
2128 // We need to generate the arrow button and related html
2129 list($sort_order, $order_img) = $this->_getSortingUrlParams(
2130 $sort_direction, $sort_order, $column_index, $index
2132 $order_img .= " <small>" . ($index + 1) . "</small>";
2133 } else {
2134 $sort_order .= strtoupper($sort_direction[$index]);
2136 // Separate columns by a comma
2137 $sort_order .= ", ";
2139 unset($name_to_use_in_sort);
2141 // remove the comma from the last column name in the newly
2142 // constructed clause
2143 $sort_order = mb_substr(
2144 $sort_order,
2146 mb_strlen($sort_order)-2
2148 if (empty($order_img)) {
2149 $order_img = '';
2151 return array($single_sort_order, $sort_order, $order_img);
2155 * Check whether the column is sorted
2157 * @param array $sort_expression sort expression
2158 * @param array $sort_expression_nodirection sort expression without direction
2159 * @param string $sort_tbl the table name
2160 * @param string $name_to_use_in_sort the sorting column name
2162 * @return boolean $is_in_sort the column sorted or not
2164 * @access private
2166 * @see _getTableHeaders()
2168 private function _isInSorted(
2169 $sort_expression, $sort_expression_nodirection, $sort_tbl,
2170 $name_to_use_in_sort
2173 $index_in_expression = 0;
2175 foreach ($sort_expression_nodirection as $index => $clause) {
2176 if (mb_strpos($clause, '.') !== false) {
2177 $fragments = explode('.', $clause);
2178 $clause2 = $fragments[0] . "." . str_replace('`', '', $fragments[1]);
2179 } else {
2180 $clause2 = $sort_tbl . str_replace('`', '', $clause);
2182 if ($clause2 === $sort_tbl . $name_to_use_in_sort) {
2183 $index_in_expression = $index;
2184 break;
2187 if (empty($sort_expression[$index_in_expression])) {
2188 $is_in_sort = false;
2189 } else {
2190 // Field name may be preceded by a space, or any number
2191 // of characters followed by a dot (tablename.fieldname)
2192 // so do a direct comparison for the sort expression;
2193 // this avoids problems with queries like
2194 // "SELECT id, count(id)..." and clicking to sort
2195 // on id or on count(id).
2196 // Another query to test this:
2197 // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p
2198 // (and try clicking on each column's header twice)
2199 $noSortTable = empty($sort_tbl) || mb_strpos(
2200 $sort_expression_nodirection[$index_in_expression], $sort_tbl
2201 ) === false;
2202 $noOpenParenthesis = mb_strpos(
2203 $sort_expression_nodirection[$index_in_expression], '('
2204 ) === false;
2205 if (! empty($sort_tbl) && $noSortTable && $noOpenParenthesis) {
2206 $new_sort_expression_nodirection = $sort_tbl
2207 . $sort_expression_nodirection[$index_in_expression];
2208 } else {
2209 $new_sort_expression_nodirection
2210 = $sort_expression_nodirection[$index_in_expression];
2213 //Back quotes are removed in next comparison, so remove them from value
2214 //to compare.
2215 $name_to_use_in_sort = str_replace('`', '', $name_to_use_in_sort);
2217 $is_in_sort = false;
2218 $sort_name = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort;
2220 if ($sort_name == str_replace('`', '', $new_sort_expression_nodirection)
2221 || $sort_name == str_replace('`', '', $sort_expression_nodirection[$index_in_expression])
2223 $is_in_sort = true;
2227 return $is_in_sort;
2229 } // end of the '_isInSorted()' function
2233 * Get sort url parameters - sort order and order image
2235 * @param array $sort_direction the sort direction
2236 * @param string $sort_order the sorting order
2237 * @param integer $column_index the index of the column
2238 * @param integer $index the index of sort direction array.
2240 * @return array 2 element array - $sort_order, $order_img
2242 * @access private
2244 * @see _getSingleAndMultiSortUrls()
2246 private function _getSortingUrlParams(
2247 $sort_direction, $sort_order, $column_index, $index
2249 if (strtoupper(trim($sort_direction[$index])) == self::DESCENDING_SORT_DIR) {
2250 $sort_order .= ' ASC';
2251 $order_img = ' ' . Util::getImage(
2252 's_desc.png', __('Descending'),
2253 array('class' => "soimg$column_index", 'title' => '')
2255 $order_img .= ' ' . Util::getImage(
2256 's_asc.png', __('Ascending'),
2257 array('class' => "soimg$column_index hide", 'title' => '')
2259 } else {
2260 $sort_order .= ' DESC';
2261 $order_img = ' ' . Util::getImage(
2262 's_asc.png', __('Ascending'),
2263 array('class' => "soimg$column_index", 'title' => '')
2265 $order_img .= ' ' . Util::getImage(
2266 's_desc.png', __('Descending'),
2267 array('class' => "soimg$column_index hide", 'title' => '')
2270 return array($sort_order, $order_img);
2271 } // end of the '_getSortingUrlParams()' function
2275 * Get sort order link
2277 * @param string $order_img the sort order image
2278 * @param integer $col_index the index of the column
2279 * @param array $fields_meta set of field properties
2280 * @param string $order_url the url for sort
2281 * @param string $multi_order_url the url for sort
2283 * @return string the sort order link
2285 * @access private
2287 * @see _getTableHeaders()
2289 private function _getSortOrderLink(
2290 $order_img, $col_index,
2291 $fields_meta, $order_url, $multi_order_url
2293 $order_link_params = array();
2294 if (isset($order_img) && ($order_img != '')) {
2295 if (mb_strstr($order_img, 'asc')) {
2296 $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()";
2297 $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()";
2298 } elseif (mb_strstr($order_img, 'desc')) {
2299 $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()";
2300 $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()";
2304 $order_link_content = htmlspecialchars($fields_meta->name);
2305 $inner_link_content = $order_link_content . $order_img
2306 . '<input type="hidden" value="' . $multi_order_url . '" />';
2308 return Util::linkOrButton(
2309 $order_url, $inner_link_content,
2310 $order_link_params, false, true
2313 } // end of the '_getSortOrderLink()' function
2316 * Check if the column contains numeric data. If yes, then set the
2317 * column header's alignment right
2319 * @param array $fields_meta set of field properties
2320 * @param array &$th_class array containing classes
2322 * @return void
2324 * @see _getDraggableClassForSortableColumns()
2326 private function _getClassForNumericColumnType($fields_meta,&$th_class)
2328 if (preg_match(
2329 '@int|decimal|float|double|real|bit|boolean|serial@i',
2330 $fields_meta->type
2331 )) {
2332 $th_class[] = 'right';
2337 * Prepare columns to draggable effect for sortable columns
2339 * @param boolean $col_visib the column is visible (false)
2340 * array the column is not visible (string array)
2341 * @param string $col_visib_j element of $col_visib array
2342 * @param array $fields_meta set of field properties
2343 * @param string $order_link the order link
2344 * @param string $comments the comment for the column
2346 * @return string $draggable_html html content
2348 * @access private
2350 * @see _getTableHeaders()
2352 private function _getDraggableClassForSortableColumns(
2353 $col_visib, $col_visib_j, $fields_meta,
2354 $order_link, $comments
2357 $draggable_html = '<th';
2358 $th_class = array();
2359 $th_class[] = 'draggable';
2360 $this->_getClassForNumericColumnType($fields_meta, $th_class);
2361 if ($col_visib && !$col_visib_j) {
2362 $th_class[] = 'hide';
2365 $th_class[] = 'column_heading';
2366 if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) {
2367 $th_class[] = 'pointer';
2370 if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) {
2371 $th_class[] = 'marker';
2374 $draggable_html .= ' class="' . implode(' ', $th_class) . '"';
2376 $draggable_html .= ' data-column="' . htmlspecialchars($fields_meta->name)
2377 . '">' . $order_link . $comments . '</th>';
2379 return $draggable_html;
2381 } // end of the '_getDraggableClassForSortableColumns()' function
2385 * Prepare columns to draggable effect for non sortable columns
2387 * @param boolean $col_visib the column is visible (false)
2388 * array the column is not visible (string array)
2389 * @param string $col_visib_j element of $col_visib array
2390 * @param boolean $condition_field whether to add CSS class condition
2391 * @param array $fields_meta set of field properties
2392 * @param string $comments the comment for the column
2394 * @return string $draggable_html html content
2396 * @access private
2398 * @see _getTableHeaders()
2400 private function _getDraggableClassForNonSortableColumns(
2401 $col_visib, $col_visib_j, $condition_field,
2402 $fields_meta, $comments
2405 $draggable_html = '<th';
2406 $th_class = array();
2407 $th_class[] = 'draggable';
2408 $this->_getClassForNumericColumnType($fields_meta, $th_class);
2409 if ($col_visib && !$col_visib_j) {
2410 $th_class[] = 'hide';
2413 if ($condition_field) {
2414 $th_class[] = 'condition';
2417 $draggable_html .= ' class="' . implode(' ', $th_class) . '"';
2419 $draggable_html .= ' data-column="'
2420 . htmlspecialchars($fields_meta->name) . '">';
2422 $draggable_html .= htmlspecialchars($fields_meta->name);
2424 $draggable_html .= "\n" . $comments . '</th>';
2426 return $draggable_html;
2428 } // end of the '_getDraggableClassForNonSortableColumns()' function
2432 * Prepare column to show at right side - check boxes or empty column
2434 * @param array &$displayParts which elements to display
2435 * @param string $full_or_partial_text_link full/partial link or text button
2436 * @param string $colspan column span of table header
2438 * @return string html content
2440 * @access private
2442 * @see _getTableHeaders()
2444 private function _getColumnAtRightSide(
2445 &$displayParts, $full_or_partial_text_link, $colspan
2448 $right_column_html = '';
2449 $display_params = $this->__get('display_params');
2451 // Displays the needed checkboxes at the right
2452 // column of the result table header if possible and required...
2453 if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT)
2454 || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
2455 && (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
2456 || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE))
2457 && ($displayParts['text_btn'] == '1')
2460 $display_params['emptyafter']
2461 = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
2462 && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 1;
2464 $right_column_html .= "\n"
2465 . '<th class="column_action print_ignore" ' . $colspan . '>'
2466 . $full_or_partial_text_link
2467 . '</th>';
2469 } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
2470 || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH))
2471 && (($displayParts['edit_lnk'] == self::NO_EDIT_OR_DELETE)
2472 && ($displayParts['del_lnk'] == self::NO_EDIT_OR_DELETE))
2473 && (! isset($GLOBALS['is_header_sent']) || ! $GLOBALS['is_header_sent'])
2475 // ... elseif no button, displays empty columns if required
2476 // (unless coming from Browse mode print view)
2478 $display_params['emptyafter']
2479 = (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
2480 && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 1;
2482 $right_column_html .= "\n" . '<td class="print_ignore" ' . $colspan
2483 . '></td>';
2486 $this->__set('display_params', $display_params);
2488 return $right_column_html;
2490 } // end of the '_getColumnAtRightSide()' function
2494 * Prepares the display for a value
2496 * @param string $class class of table cell
2497 * @param bool $condition_field whether to add CSS class condition
2498 * @param string $value value to display
2500 * @return string the td
2502 * @access private
2504 * @see _getDataCellForGeometryColumns(),
2505 * _getDataCellForNonNumericColumns()
2507 private function _buildValueDisplay($class, $condition_field, $value)
2509 return '<td class="left ' . $class . ($condition_field ? ' condition' : '')
2510 . '">' . $value . '</td>';
2511 } // end of the '_buildValueDisplay()' function
2515 * Prepares the display for a null value
2517 * @param string $class class of table cell
2518 * @param bool $condition_field whether to add CSS class condition
2519 * @param object $meta the meta-information about this field
2520 * @param string $align cell alignment
2522 * @return string the td
2524 * @access private
2526 * @see _getDataCellForNumericColumns(),
2527 * _getDataCellForGeometryColumns(),
2528 * _getDataCellForNonNumericColumns()
2530 private function _buildNullDisplay($class, $condition_field, $meta, $align = '')
2532 // the null class is needed for grid editing
2533 $decimals = isset($meta->decimals) ? $meta->decimals : '-1';
2534 return '<td ' . $align . ' data-decimals="' . $decimals
2535 . '" data-type="' . $meta->type . '" class="'
2536 . $this->_addClass(
2537 $class, $condition_field, $meta, ''
2539 . ' null"><i>NULL</i></td>';
2540 } // end of the '_buildNullDisplay()' function
2544 * Prepares the display for an empty value
2546 * @param string $class class of table cell
2547 * @param bool $condition_field whether to add CSS class condition
2548 * @param object $meta the meta-information about this field
2549 * @param string $align cell alignment
2551 * @return string the td
2553 * @access private
2555 * @see _getDataCellForNumericColumns(),
2556 * _getDataCellForGeometryColumns(),
2557 * _getDataCellForNonNumericColumns()
2559 private function _buildEmptyDisplay($class, $condition_field, $meta, $align = '')
2561 return '<td ' . $align . ' class="'
2562 . $this->_addClass(
2563 $class, $condition_field, $meta, 'nowrap'
2565 . '"></td>';
2566 } // end of the '_buildEmptyDisplay()' function
2570 * Adds the relevant classes.
2572 * @param string $class class of table cell
2573 * @param bool $condition_field whether to add CSS class
2574 * condition
2575 * @param object $meta the meta-information about the
2576 * field
2577 * @param string $nowrap avoid wrapping
2578 * @param bool $is_field_truncated is field truncated (display ...)
2579 * @param object|string $transformation_plugin transformation plugin.
2580 * Can also be the default function:
2581 * PMA_mimeDefaultFunction
2582 * @param string $default_function default transformation function
2584 * @return string the list of classes
2586 * @access private
2588 * @see _buildNullDisplay(), _getRowData()
2590 private function _addClass(
2591 $class, $condition_field, $meta, $nowrap, $is_field_truncated = false,
2592 $transformation_plugin = '', $default_function = ''
2594 $classes = array(
2595 $class,
2596 $nowrap,
2599 if (isset($meta->mimetype)) {
2600 $classes[] = preg_replace('/\//', '_', $meta->mimetype);
2603 if ($condition_field) {
2604 $classes[] = 'condition';
2607 if ($is_field_truncated) {
2608 $classes[] = 'truncated';
2611 $mime_map = $this->__get('mime_map');
2612 $orgFullColName = $this->__get('db') . '.' . $meta->orgtable
2613 . '.' . $meta->orgname;
2614 if ($transformation_plugin != $default_function
2615 || !empty($mime_map[$orgFullColName]['input_transformation'])
2617 $classes[] = 'transformed';
2620 // Define classes to be added to this data field based on the type of data
2621 $matches = array(
2622 'enum' => 'enum',
2623 'set' => 'set',
2624 'binary' => 'hex',
2627 foreach ($matches as $key => $value) {
2628 if (mb_strpos($meta->flags, $key) !== false) {
2629 $classes[] = $value;
2633 if (mb_strpos($meta->type, 'bit') !== false) {
2634 $classes[] = 'bit';
2637 return implode(' ', $classes);
2638 } // end of the '_addClass()' function
2641 * Prepare the body of the results table
2643 * @param integer &$dt_result the link id associated to the query
2644 * which results have to be displayed which
2645 * results have to be displayed
2646 * @param array &$displayParts which elements to display
2647 * @param array $map the list of relations
2648 * @param array $analyzed_sql_results analyzed sql results
2649 * @param boolean $is_limited_display with limited operations or not
2651 * @return string $table_body_html html content
2653 * @global array $row current row data
2655 * @access private
2657 * @see getTable()
2659 private function _getTableBody(
2660 &$dt_result, &$displayParts, $map, $analyzed_sql_results,
2661 $is_limited_display = false
2664 global $row; // mostly because of browser transformations,
2665 // to make the row-data accessible in a plugin
2667 $table_body_html = '';
2669 // query without conditions to shorten URLs when needed, 200 is just
2670 // guess, it should depend on remaining URL length
2671 $url_sql_query = $this->_getUrlSqlQuery($analyzed_sql_results);
2673 $display_params = $this->__get('display_params');
2675 if (! is_array($map)) {
2676 $map = array();
2679 $row_no = 0;
2680 $display_params['edit'] = array();
2681 $display_params['copy'] = array();
2682 $display_params['delete'] = array();
2683 $display_params['data'] = array();
2684 $display_params['row_delete'] = array();
2685 $this->__set('display_params', $display_params);
2687 // name of the class added to all grid editable elements;
2688 // if we don't have all the columns of a unique key in the result set,
2689 // do not permit grid editing
2690 if ($is_limited_display || ! $this->__get('editable')) {
2691 $grid_edit_class = '';
2692 } else {
2693 switch ($GLOBALS['cfg']['GridEditing']) {
2694 case 'double-click':
2695 // trying to reduce generated HTML by using shorter
2696 // classes like click1 and click2
2697 $grid_edit_class = 'grid_edit click2';
2698 break;
2699 case 'click':
2700 $grid_edit_class = 'grid_edit click1';
2701 break;
2702 default: // 'disabled'
2703 $grid_edit_class = '';
2704 break;
2708 // prepare to get the column order, if available
2709 list($col_order, $col_visib) = $this->_getColumnParams(
2710 $analyzed_sql_results
2713 // Correction University of Virginia 19991216 in the while below
2714 // Previous code assumed that all tables have keys, specifically that
2715 // the phpMyAdmin GUI should support row delete/edit only for such
2716 // tables.
2717 // Although always using keys is arguably the prescribed way of
2718 // defining a relational table, it is not required. This will in
2719 // particular be violated by the novice.
2720 // We want to encourage phpMyAdmin usage by such novices. So the code
2721 // below has been changed to conditionally work as before when the
2722 // table being displayed has one or more keys; but to display
2723 // delete/edit options correctly for tables without keys.
2725 $odd_row = true;
2727 $whereClauseMap = $this->__get('whereClauseMap');
2728 while ($row = $GLOBALS['dbi']->fetchRow($dt_result)) {
2730 // add repeating headers
2731 if ((($row_no != 0) && ($_SESSION['tmpval']['repeat_cells'] != 0))
2732 && !($row_no % $_SESSION['tmpval']['repeat_cells'])
2734 $table_body_html .= $this->_getRepeatingHeaders(
2735 $display_params
2739 $tr_class = array();
2740 if ($GLOBALS['cfg']['BrowsePointerEnable'] != true) {
2741 $tr_class[] = 'nopointer';
2743 if ($GLOBALS['cfg']['BrowseMarkerEnable'] != true) {
2744 $tr_class[] = 'nomarker';
2746 $tr_class[] = ($odd_row ? 'odd' : 'even');
2747 $odd_row = ! $odd_row;
2749 // pointer code part
2750 $table_body_html .= '<tr class="' . implode(' ', $tr_class) . '">';
2752 // 1. Prepares the row
2754 // In print view these variable needs to be initialized
2755 $del_url = $del_str = $edit_anchor_class
2756 = $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null;
2758 // 1.2 Defines the URLs for the modify/delete link(s)
2760 if (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
2761 || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
2764 // Results from a "SELECT" statement -> builds the
2765 // WHERE clause to use in links (a unique key if possible)
2767 * @todo $where_clause could be empty, for example a table
2768 * with only one field and it's a BLOB; in this case,
2769 * avoid to display the delete and edit links
2771 list($where_clause, $clause_is_unique, $condition_array)
2772 = Util::getUniqueCondition(
2773 $dt_result, // handle
2774 $this->__get('fields_cnt'), // fields_cnt
2775 $this->__get('fields_meta'), // fields_meta
2776 $row, // row
2777 false, // force_unique
2778 $this->__get('table'), // restrict_to_table
2779 $analyzed_sql_results // analyzed_sql_results
2781 $whereClauseMap[$row_no][$this->__get('table')] = $where_clause;
2782 $this->__set('whereClauseMap', $whereClauseMap);
2784 $where_clause_html = urlencode($where_clause);
2786 // 1.2.1 Modify link(s) - update row case
2787 if ($displayParts['edit_lnk'] == self::UPDATE_ROW) {
2789 list($edit_url, $copy_url, $edit_str, $copy_str,
2790 $edit_anchor_class)
2791 = $this->_getModifiedLinks(
2792 $where_clause,
2793 $clause_is_unique, $url_sql_query
2796 } // end if (1.2.1)
2798 // 1.2.2 Delete/Kill link(s)
2799 list($del_url, $del_str, $js_conf)
2800 = $this->_getDeleteAndKillLinks(
2801 $where_clause, $clause_is_unique,
2802 $url_sql_query, $displayParts['del_lnk'],
2803 $row
2806 // 1.3 Displays the links at left if required
2807 if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT)
2808 || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)
2811 $table_body_html .= $this->_getPlacedLinks(
2812 self::POSITION_LEFT, $del_url, $displayParts, $row_no,
2813 $where_clause, $where_clause_html, $condition_array,
2814 $edit_url, $copy_url, $edit_anchor_class,
2815 $edit_str, $copy_str, $del_str, $js_conf
2818 } elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) {
2820 $table_body_html .= $this->_getPlacedLinks(
2821 self::POSITION_NONE, $del_url, $displayParts, $row_no,
2822 $where_clause, $where_clause_html, $condition_array,
2823 $edit_url, $copy_url, $edit_anchor_class,
2824 $edit_str, $copy_str, $del_str, $js_conf
2827 } // end if (1.3)
2828 } // end if (1)
2830 // 2. Displays the rows' values
2831 if (is_null($this->__get('mime_map'))) {
2832 $this->_setMimeMap();
2834 $table_body_html .= $this->_getRowValues(
2835 $dt_result,
2836 $row,
2837 $row_no,
2838 $col_order,
2839 $map,
2840 $grid_edit_class,
2841 $col_visib,
2842 $url_sql_query,
2843 $analyzed_sql_results
2846 // 3. Displays the modify/delete links on the right if required
2847 if (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
2848 || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
2850 if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT)
2851 || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)
2854 $table_body_html .= $this->_getPlacedLinks(
2855 self::POSITION_RIGHT, $del_url, $displayParts, $row_no,
2856 $where_clause, $where_clause_html, $condition_array,
2857 $edit_url, $copy_url, $edit_anchor_class,
2858 $edit_str, $copy_str, $del_str, $js_conf
2862 } // end if (3)
2864 $table_body_html .= '</tr>';
2865 $table_body_html .= "\n";
2866 $row_no++;
2868 } // end while
2870 return $table_body_html;
2872 } // end of the '_getTableBody()' function
2875 * Sets the MIME details of the columns in the results set
2877 * @return void
2879 private function _setMimeMap()
2881 $fields_meta = $this->__get('fields_meta');
2882 $mimeMap = array();
2883 $added = array();
2885 for ($currentColumn = 0;
2886 $currentColumn < $this->__get('fields_cnt');
2887 ++$currentColumn) {
2889 $meta = $fields_meta[$currentColumn];
2890 $orgFullTableName = $this->__get('db') . '.' . $meta->orgtable;
2892 if ($GLOBALS['cfgRelation']['commwork']
2893 && $GLOBALS['cfgRelation']['mimework']
2894 && $GLOBALS['cfg']['BrowseMIME']
2895 && ! $_SESSION['tmpval']['hide_transformation']
2896 && empty($added[$orgFullTableName])
2898 $mimeMap = array_merge(
2899 $mimeMap,
2900 PMA_getMIME($this->__get('db'), $meta->orgtable, false, true)
2902 $added[$orgFullTableName] = true;
2906 // special browser transformation for some SHOW statements
2907 if ($this->__get('is_show')
2908 && ! $_SESSION['tmpval']['hide_transformation']
2910 preg_match(
2911 '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
2912 . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
2913 . ')@i',
2914 $this->__get('sql_query'), $which
2917 if (isset($which[1])) {
2918 $str = ' ' . strtoupper($which[1]);
2919 $isShowProcessList = strpos($str, 'PROCESSLIST') > 0;
2920 if ($isShowProcessList) {
2921 $mimeMap['..Info'] = array(
2922 'mimetype' => 'Text_Plain',
2923 'transformation' => 'output/Text_Plain_Sql.php',
2927 $isShowCreateTable = preg_match(
2928 '@CREATE[[:space:]]+TABLE@i', $this->__get('sql_query')
2930 if ($isShowCreateTable) {
2931 $mimeMap['..Create Table'] = array(
2932 'mimetype' => 'Text_Plain',
2933 'transformation' => 'output/Text_Plain_Sql.php',
2939 $this->__set('mime_map', $mimeMap);
2943 * Get the values for one data row
2945 * @param integer &$dt_result the link id associated to
2946 * the query which results
2947 * have to be displayed which
2948 * results have to be
2949 * displayed
2950 * @param array $row current row data
2951 * @param integer $row_no the index of current row
2952 * @param array $col_order the column order false when
2953 * a property not found false
2954 * when a property not found
2955 * @param array $map the list of relations
2956 * @param string $grid_edit_class the class for all editable
2957 * columns
2958 * @param boolean|array|string $col_visib column is visible(false);
2959 * column isn't visible(string
2960 * array)
2961 * @param string $url_sql_query the analyzed sql query
2962 * @param array $analyzed_sql_results analyzed sql results
2964 * @return string $row_values_html html content
2966 * @access private
2968 * @see _getTableBody()
2970 private function _getRowValues(
2971 &$dt_result, $row, $row_no, $col_order, $map,
2972 $grid_edit_class, $col_visib,
2973 $url_sql_query, $analyzed_sql_results
2975 $row_values_html = '';
2977 // Following variable are needed for use in isset/empty or
2978 // use with array indexes/safe use in foreach
2979 $sql_query = $this->__get('sql_query');
2980 $fields_meta = $this->__get('fields_meta');
2981 $highlight_columns = $this->__get('highlight_columns');
2982 $mime_map = $this->__get('mime_map');
2984 $row_info = $this->_getRowInfoForSpecialLinks($row, $col_order);
2986 $whereClauseMap = $this->__get('whereClauseMap');
2988 $columnCount = $this->__get('fields_cnt');
2989 for ($currentColumn = 0;
2990 $currentColumn < $columnCount;
2991 ++$currentColumn) {
2993 // assign $i with appropriate column order
2994 $i = $col_order ? $col_order[$currentColumn] : $currentColumn;
2996 $meta = $fields_meta[$i];
2997 $orgFullColName
2998 = $this->__get('db') . '.' . $meta->orgtable . '.' . $meta->orgname;
3000 $not_null_class = $meta->not_null ? 'not_null' : '';
3001 $relation_class = isset($map[$meta->name]) ? 'relation' : '';
3002 $hide_class = ($col_visib && ! $col_visib[$currentColumn])
3003 ? 'hide'
3004 : '';
3005 $grid_edit = $meta->orgtable != '' ? $grid_edit_class : '';
3007 // handle datetime-related class, for grid editing
3008 $field_type_class
3009 = $this->_getClassForDateTimeRelatedFields($meta->type);
3011 $is_field_truncated = false;
3012 // combine all the classes applicable to this column's value
3013 $class = $this->_getClassesForColumn(
3014 $grid_edit, $not_null_class, $relation_class,
3015 $hide_class, $field_type_class
3018 // See if this column should get highlight because it's used in the
3019 // where-query.
3020 $condition_field = (isset($highlight_columns)
3021 && (isset($highlight_columns[$meta->name])
3022 || isset($highlight_columns[Util::backquote($meta->name)])))
3023 ? true
3024 : false;
3026 // Wrap MIME-transformations. [MIME]
3027 $default_function = 'PMA_mimeDefaultFunction'; // default_function
3028 $transformation_plugin = $default_function;
3029 $transform_options = array();
3031 if ($GLOBALS['cfgRelation']['mimework']
3032 && $GLOBALS['cfg']['BrowseMIME']
3035 if (isset($mime_map[$orgFullColName]['mimetype'])
3036 && !empty($mime_map[$orgFullColName]['transformation'])
3039 $file = $mime_map[$orgFullColName]['transformation'];
3040 $include_file = 'libraries/plugins/transformations/' . $file;
3042 if (file_exists($include_file)) {
3044 include_once $include_file;
3045 $class_name = PMA_getTransformationClassName($include_file);
3046 // todo add $plugin_manager
3047 $plugin_manager = null;
3048 $transformation_plugin = new $class_name(
3049 $plugin_manager
3052 $transform_options = PMA_Transformation_getOptions(
3053 isset(
3054 $mime_map[$orgFullColName]
3055 ['transformation_options']
3057 ? $mime_map[$orgFullColName]
3058 ['transformation_options']
3059 : ''
3062 $meta->mimetype = str_replace(
3063 '_', '/',
3064 $mime_map[$orgFullColName]['mimetype']
3067 } // end if file_exists
3068 } // end if transformation is set
3069 } // end if mime/transformation works.
3071 // Check whether the field needs to display with syntax highlighting
3073 $dbLower = mb_strtolower($this->__get('db'));
3074 $tblLower = mb_strtolower($meta->orgtable);
3075 $nameLower = mb_strtolower($meta->orgname);
3076 if (! empty($this->transformation_info[$dbLower][$tblLower][$nameLower])
3077 && (trim($row[$i]) != '')
3078 && ! $_SESSION['tmpval']['hide_transformation']
3080 include_once $this->transformation_info
3081 [$dbLower][$tblLower][$nameLower][0];
3082 $transformation_plugin = new $this->transformation_info
3083 [$dbLower][$tblLower][$nameLower][1](null);
3085 $transform_options = PMA_Transformation_getOptions(
3086 isset($mime_map[$orgFullColName]['transformation_options'])
3087 ? $mime_map[$orgFullColName]['transformation_options']
3088 : ''
3091 $meta->mimetype = str_replace(
3092 '_', '/',
3093 $this->transformation_info[$dbLower]
3094 [mb_strtolower($meta->orgtable)]
3095 [mb_strtolower($meta->orgname)][2]
3100 // Check for the predefined fields need to show as link in schemas
3101 include_once 'libraries/special_schema_links.lib.php';
3103 if (isset($GLOBALS['special_schema_links'])
3104 && (! empty($GLOBALS['special_schema_links'][$dbLower][$tblLower][$nameLower]))
3107 $linking_url = $this->_getSpecialLinkUrl(
3108 $row[$i], $row_info, mb_strtolower($meta->orgname)
3110 $transformation_plugin = new Text_Plain_Link();
3112 $transform_options = array(
3113 0 => $linking_url,
3114 2 => true
3117 $meta->mimetype = str_replace(
3118 '_', '/',
3119 'Text/Plain'
3125 * The result set can have columns from more than one table,
3126 * this is why we have to check for the unique conditions
3127 * related to this table; however getUniqueCondition() is
3128 * costly and does not need to be called if we already know
3129 * the conditions for the current table.
3131 if (! isset($whereClauseMap[$row_no][$meta->orgtable])) {
3132 $unique_conditions = Util::getUniqueCondition(
3133 $dt_result, // handle
3134 $this->__get('fields_cnt'), // fields_cnt
3135 $this->__get('fields_meta'), // fields_meta
3136 $row, // row
3137 false, // force_unique
3138 $meta->orgtable, // restrict_to_table
3139 $analyzed_sql_results // analyzed_sql_results
3141 $whereClauseMap[$row_no][$meta->orgtable] = $unique_conditions[0];
3144 $_url_params = array(
3145 'db' => $this->__get('db'),
3146 'table' => $meta->orgtable,
3147 'where_clause' => $whereClauseMap[$row_no][$meta->orgtable],
3148 'transform_key' => $meta->orgname
3151 if (! empty($sql_query)) {
3152 $_url_params['sql_query'] = $url_sql_query;
3155 $transform_options['wrapper_link']
3156 = PMA_URL_getCommon($_url_params);
3158 $display_params = $this->__get('display_params');
3160 // in some situations (issue 11406), numeric returns 1
3161 // even for a string type
3162 if ($meta->numeric == 1 && $meta->type != 'string') {
3163 // n u m e r i c
3165 $display_params['data'][$row_no][$i]
3166 = $this->_getDataCellForNumericColumns(
3167 $row[$i],
3168 $class,
3169 $condition_field,
3170 $meta,
3171 $map,
3172 $is_field_truncated,
3173 $analyzed_sql_results,
3174 $transformation_plugin,
3175 $default_function,
3176 $transform_options
3179 } elseif ($meta->type == self::GEOMETRY_FIELD) {
3180 // g e o m e t r y
3182 // Remove 'grid_edit' from $class as we do not allow to
3183 // inline-edit geometry data.
3184 $class = str_replace('grid_edit', '', $class);
3186 $display_params['data'][$row_no][$i]
3187 = $this->_getDataCellForGeometryColumns(
3188 $row[$i],
3189 $class,
3190 $meta,
3191 $map,
3192 $_url_params,
3193 $condition_field,
3194 $transformation_plugin,
3195 $default_function,
3196 $transform_options,
3197 $analyzed_sql_results
3200 } else {
3201 // n o t n u m e r i c
3203 $display_params['data'][$row_no][$i]
3204 = $this->_getDataCellForNonNumericColumns(
3205 $row[$i],
3206 $class,
3207 $meta,
3208 $map,
3209 $_url_params,
3210 $condition_field,
3211 $transformation_plugin,
3212 $default_function,
3213 $transform_options,
3214 $is_field_truncated,
3215 $analyzed_sql_results,
3216 $dt_result,
3222 // output stored cell
3223 $row_values_html .= $display_params['data'][$row_no][$i];
3225 if (isset($display_params['rowdata'][$i][$row_no])) {
3226 $display_params['rowdata'][$i][$row_no]
3227 .= $display_params['data'][$row_no][$i];
3228 } else {
3229 $display_params['rowdata'][$i][$row_no]
3230 = $display_params['data'][$row_no][$i];
3233 $this->__set('display_params', $display_params);
3235 } // end for
3237 return $row_values_html;
3239 } // end of the '_getRowValues()' function
3242 * Get link for display special schema links
3244 * @param string $column_value column value
3245 * @param array $row_info information about row
3246 * @param string $field_name column name
3248 * @return string generated link
3250 private function _getSpecialLinkUrl($column_value, $row_info, $field_name)
3253 $linking_url_params = array();
3254 $link_relations = $GLOBALS['special_schema_links']
3255 [mb_strtolower($this->__get('db'))]
3256 [mb_strtolower($this->__get('table'))]
3257 [$field_name];
3259 if (! is_array($link_relations['link_param'])) {
3260 $linking_url_params[$link_relations['link_param']] = $column_value;
3261 } else {
3262 // Consider only the case of creating link for column field
3263 // sql query that needs to be passed as url param
3264 $sql = 'SELECT `' . $column_value . '` FROM `'
3265 . $row_info[$link_relations['link_param'][1]] . '`.`'
3266 . $row_info[$link_relations['link_param'][2]] . '`';
3267 $linking_url_params[$link_relations['link_param'][0]] = $sql;
3270 $divider = strpos($link_relations['default_page'], '?') ? '&' : '?';
3271 if (empty($link_relations['link_dependancy_params'])) {
3272 return $link_relations['default_page']
3273 . PMA_URL_getCommon($linking_url_params, 'html', $divider);
3276 foreach ($link_relations['link_dependancy_params'] as $new_param) {
3278 // If param_info is an array, set the key and value
3279 // from that array
3280 if (is_array($new_param['param_info'])) {
3281 $linking_url_params[$new_param['param_info'][0]]
3282 = $new_param['param_info'][1];
3283 continue;
3286 $linking_url_params[$new_param['param_info']]
3287 = $row_info[mb_strtolower($new_param['column_name'])];
3289 // Special case 1 - when executing routines, according
3290 // to the type of the routine, url param changes
3291 if (empty($row_info['routine_type'])) {
3292 continue;
3296 return $link_relations['default_page']
3297 . PMA_URL_getCommon($linking_url_params, 'html', $divider);
3302 * Prepare row information for display special links
3304 * @param array $row current row data
3305 * @param array $col_order the column order
3307 * @return array $row_info associative array with column nama -> value
3309 private function _getRowInfoForSpecialLinks($row, $col_order)
3312 $row_info = array();
3313 $fields_meta = $this->__get('fields_meta');
3315 for ($n = 0; $n < $this->__get('fields_cnt'); ++$n) {
3316 $m = $col_order ? $col_order[$n] : $n;
3317 $row_info[mb_strtolower($fields_meta[$m]->name)]
3318 = $row[$m];
3321 return $row_info;
3326 * Get url sql query without conditions to shorten URLs
3328 * @param array $analyzed_sql_results analyzed sql results
3330 * @return string $url_sql analyzed sql query
3332 * @access private
3334 * @see _getTableBody()
3336 private function _getUrlSqlQuery($analyzed_sql_results)
3338 if (($analyzed_sql_results['querytype'] != 'SELECT')
3339 || (mb_strlen($this->__get('sql_query')) < 200)
3341 return $this->__get('sql_query');
3344 $query = 'SELECT ' . Query::getClause(
3345 $analyzed_sql_results['statement'],
3346 $analyzed_sql_results['parser']->list,
3347 'SELECT'
3350 $from_clause = Query::getClause(
3351 $analyzed_sql_results['statement'],
3352 $analyzed_sql_results['parser']->list,
3353 'FROM'
3356 if (!empty($from_clause)) {
3357 $query .= ' FROM ' . $from_clause;
3360 return $query;
3362 } // end of the '_getUrlSqlQuery()' function
3366 * Get column order and column visibility
3368 * @param array $analyzed_sql_results analyzed sql results
3370 * @return array 2 element array - $col_order, $col_visib
3372 * @access private
3374 * @see _getTableBody()
3376 private function _getColumnParams($analyzed_sql_results)
3378 if ($this->_isSelect($analyzed_sql_results)) {
3379 $pmatable = new Table($this->__get('table'), $this->__get('db'));
3380 $col_order = $pmatable->getUiProp(Table::PROP_COLUMN_ORDER);
3381 $col_visib = $pmatable->getUiProp(Table::PROP_COLUMN_VISIB);
3382 } else {
3383 $col_order = false;
3384 $col_visib = false;
3387 return array($col_order, $col_visib);
3388 } // end of the '_getColumnParams()' function
3392 * Get HTML for repeating headers
3394 * @param array $display_params holds various display info
3396 * @return string $header_html html content
3398 * @access private
3400 * @see _getTableBody()
3402 private function _getRepeatingHeaders(
3403 $display_params
3405 $header_html = '<tr>' . "\n";
3407 if ($display_params['emptypre'] > 0) {
3409 $header_html .= ' <th colspan="'
3410 . $display_params['emptypre'] . '">'
3411 . "\n" . ' &nbsp;</th>' . "\n";
3413 } else if ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) {
3414 $header_html .= ' <th></th>' . "\n";
3417 foreach ($display_params['desc'] as $val) {
3418 $header_html .= $val;
3421 if ($display_params['emptyafter'] > 0) {
3422 $header_html
3423 .= ' <th colspan="' . $display_params['emptyafter']
3424 . '">'
3425 . "\n" . ' &nbsp;</th>' . "\n";
3427 $header_html .= '</tr>' . "\n";
3429 return $header_html;
3431 } // end of the '_getRepeatingHeaders()' function
3435 * Get modified links
3437 * @param string $where_clause the where clause of the sql
3438 * @param boolean $clause_is_unique the unique condition of clause
3439 * @param string $url_sql_query the analyzed sql query
3441 * @return array 5 element array - $edit_url, $copy_url,
3442 * $edit_str, $copy_str, $edit_anchor_class
3444 * @access private
3446 * @see _getTableBody()
3448 private function _getModifiedLinks(
3449 $where_clause, $clause_is_unique, $url_sql_query
3452 $_url_params = array(
3453 'db' => $this->__get('db'),
3454 'table' => $this->__get('table'),
3455 'where_clause' => $where_clause,
3456 'clause_is_unique' => $clause_is_unique,
3457 'sql_query' => $url_sql_query,
3458 'goto' => 'sql.php',
3461 $edit_url = 'tbl_change.php'
3462 . PMA_URL_getCommon(
3463 $_url_params + array('default_action' => 'update')
3466 $copy_url = 'tbl_change.php'
3467 . PMA_URL_getCommon(
3468 $_url_params + array('default_action' => 'insert')
3471 $edit_str = $this->_getActionLinkContent(
3472 'b_edit.png', __('Edit')
3474 $copy_str = $this->_getActionLinkContent(
3475 'b_insrow.png', __('Copy')
3478 // Class definitions required for grid editing jQuery scripts
3479 $edit_anchor_class = "edit_row_anchor";
3480 if ($clause_is_unique == 0) {
3481 $edit_anchor_class .= ' nonunique';
3484 return array($edit_url, $copy_url, $edit_str, $copy_str, $edit_anchor_class);
3486 } // end of the '_getModifiedLinks()' function
3490 * Get delete and kill links
3492 * @param string $where_clause the where clause of the sql
3493 * @param boolean $clause_is_unique the unique condition of clause
3494 * @param string $url_sql_query the analyzed sql query
3495 * @param string $del_lnk the delete link of current row
3496 * @param array $row the current row
3498 * @return array 3 element array
3499 * $del_url, $del_str, $js_conf
3501 * @access private
3503 * @see _getTableBody()
3505 private function _getDeleteAndKillLinks(
3506 $where_clause, $clause_is_unique, $url_sql_query, $del_lnk, $row
3509 $goto = $this->__get('goto');
3511 if ($del_lnk == self::DELETE_ROW) { // delete row case
3513 $_url_params = array(
3514 'db' => $this->__get('db'),
3515 'table' => $this->__get('table'),
3516 'sql_query' => $url_sql_query,
3517 'message_to_show' => __('The row has been deleted.'),
3518 'goto' => (empty($goto) ? 'tbl_sql.php' : $goto),
3521 $lnk_goto = 'sql.php' . PMA_URL_getCommon($_url_params, 'text');
3523 $del_query = 'DELETE FROM '
3524 . Util::backquote($this->__get('table'))
3525 . ' WHERE ' . $where_clause .
3526 ($clause_is_unique ? '' : ' LIMIT 1');
3528 $_url_params = array(
3529 'db' => $this->__get('db'),
3530 'table' => $this->__get('table'),
3531 'sql_query' => $del_query,
3532 'message_to_show' => __('The row has been deleted.'),
3533 'goto' => $lnk_goto,
3535 $del_url = 'sql.php' . PMA_URL_getCommon($_url_params);
3537 $js_conf = 'DELETE FROM ' . PMA_jsFormat($this->__get('table'))
3538 . ' WHERE ' . PMA_jsFormat($where_clause, false)
3539 . ($clause_is_unique ? '' : ' LIMIT 1');
3541 $del_str = $this->_getActionLinkContent('b_drop.png', __('Delete'));
3543 } elseif ($del_lnk == self::KILL_PROCESS) { // kill process case
3545 $_url_params = array(
3546 'db' => $this->__get('db'),
3547 'table' => $this->__get('table'),
3548 'sql_query' => $url_sql_query,
3549 'goto' => 'index.php',
3552 $lnk_goto = 'sql.php'
3553 . PMA_URL_getCommon(
3554 $_url_params, 'text'
3557 $kill = $GLOBALS['dbi']->getKillQuery($row[0]);
3559 $_url_params = array(
3560 'db' => 'mysql',
3561 'sql_query' => $kill,
3562 'goto' => $lnk_goto,
3565 $del_url = 'sql.php' . PMA_URL_getCommon($_url_params);
3566 $js_conf = $kill;
3567 $del_str = Util::getIcon(
3568 'b_drop.png', __('Kill')
3570 } else {
3571 $del_url = $del_str = $js_conf = null;
3574 return array($del_url, $del_str, $js_conf);
3576 } // end of the '_getDeleteAndKillLinks()' function
3580 * Get content inside the table row action links (Edit/Copy/Delete)
3582 * @param string $icon The name of the file to get
3583 * @param string $display_text The text displaying after the image icon
3585 * @return string
3587 * @access private
3589 * @see _getModifiedLinks(), _getDeleteAndKillLinks()
3591 private function _getActionLinkContent($icon, $display_text)
3594 $linkContent = '';
3596 if (isset($GLOBALS['cfg']['RowActionType'])
3597 && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_ICONS
3600 $linkContent .= '<span class="nowrap">'
3601 . Util::getImage(
3602 $icon, $display_text
3604 . '</span>';
3606 } else if (isset($GLOBALS['cfg']['RowActionType'])
3607 && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_TEXT
3610 $linkContent .= '<span class="nowrap">' . $display_text . '</span>';
3612 } else {
3614 $linkContent .= Util::getIcon(
3615 $icon, $display_text
3620 return $linkContent;
3626 * Prepare placed links
3628 * @param string $dir the direction of links should place
3629 * @param string $del_url the url for delete row
3630 * @param array $displayParts which elements to display
3631 * @param integer $row_no the index of current row
3632 * @param string $where_clause the where clause of the sql
3633 * @param string $where_clause_html the html encoded where clause
3634 * @param array $condition_array array of keys (primary, unique, condition)
3635 * @param string $edit_url the url for edit row
3636 * @param string $copy_url the url for copy row
3637 * @param string $edit_anchor_class the class for html element for edit
3638 * @param string $edit_str the label for edit row
3639 * @param string $copy_str the label for copy row
3640 * @param string $del_str the label for delete row
3641 * @param string $js_conf text for the JS confirmation
3643 * @return string html content
3645 * @access private
3647 * @see _getTableBody()
3649 private function _getPlacedLinks(
3650 $dir, $del_url, $displayParts, $row_no, $where_clause, $where_clause_html,
3651 $condition_array, $edit_url, $copy_url,
3652 $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf
3655 if (! isset($js_conf)) {
3656 $js_conf = '';
3659 return $this->_getCheckboxAndLinks(
3660 $dir, $del_url, $displayParts,
3661 $row_no, $where_clause, $where_clause_html, $condition_array,
3662 $edit_url, $copy_url, $edit_anchor_class,
3663 $edit_str, $copy_str, $del_str, $js_conf
3666 } // end of the '_getPlacedLinks()' function
3670 * Get the combined classes for a column
3672 * @param string $grid_edit_class the class for all editable columns
3673 * @param string $not_null_class the class for not null columns
3674 * @param string $relation_class the class for relations in a column
3675 * @param string $hide_class the class for visibility of a column
3676 * @param string $field_type_class the class related to type of the field
3678 * @return string $class the combined classes
3680 * @access private
3682 * @see _getTableBody()
3684 private function _getClassesForColumn(
3685 $grid_edit_class, $not_null_class, $relation_class,
3686 $hide_class, $field_type_class
3688 $class = 'data ' . $grid_edit_class . ' ' . $not_null_class . ' '
3689 . $relation_class . ' ' . $hide_class . ' ' . $field_type_class;
3691 return $class;
3693 } // end of the '_getClassesForColumn()' function
3697 * Get class for datetime related fields
3699 * @param string $type the type of the column field
3701 * @return string $field_type_class the class for the column
3703 * @access private
3705 * @see _getTableBody()
3707 private function _getClassForDateTimeRelatedFields($type)
3709 if ((substr($type, 0, 9) == self::TIMESTAMP_FIELD)
3710 || ($type == self::DATETIME_FIELD)
3712 $field_type_class = 'datetimefield';
3713 } elseif ($type == self::DATE_FIELD) {
3714 $field_type_class = 'datefield';
3715 } elseif ($type == self::TIME_FIELD) {
3716 $field_type_class = 'timefield';
3717 } elseif ($type == self::STRING_FIELD) {
3718 $field_type_class = 'text';
3719 } else {
3720 $field_type_class = '';
3722 return $field_type_class;
3723 } // end of the '_getClassForDateTimeRelatedFields()' function
3727 * Prepare data cell for numeric type fields
3729 * @param string $column the column's value
3730 * @param string $class the html class for column
3731 * @param boolean $condition_field the column should highlighted
3732 * or not
3733 * @param object $meta the meta-information about this
3734 * field
3735 * @param array $map the list of relations
3736 * @param boolean $is_field_truncated the condition for blob data
3737 * replacements
3738 * @param array $analyzed_sql_results the analyzed query
3739 * @param object|string $transformation_plugin the name of transformation plugin
3740 * @param string $default_function the default transformation
3741 * function
3742 * @param string $transform_options the transformation parameters
3744 * @return string $cell the prepared cell, html content
3746 * @access private
3748 * @see _getTableBody()
3750 private function _getDataCellForNumericColumns(
3751 $column, $class, $condition_field, $meta, $map, $is_field_truncated,
3752 $analyzed_sql_results, $transformation_plugin, $default_function,
3753 $transform_options
3756 if (! isset($column) || is_null($column)) {
3758 $cell = $this->_buildNullDisplay(
3759 'right ' . $class, $condition_field, $meta, ''
3762 } elseif ($column != '') {
3764 $nowrap = ' nowrap';
3765 $where_comparison = ' = ' . $column;
3767 $cell = $this->_getRowData(
3768 'right ' . $class, $condition_field,
3769 $analyzed_sql_results, $meta, $map, $column,
3770 $transformation_plugin, $default_function, $nowrap,
3771 $where_comparison, $transform_options,
3772 $is_field_truncated, ''
3774 } else {
3776 $cell = $this->_buildEmptyDisplay(
3777 'right ' . $class, $condition_field, $meta, ''
3781 return $cell;
3783 } // end of the '_getDataCellForNumericColumns()' function
3787 * Get data cell for geometry type fields
3789 * @param string $column the relevant column in data row
3790 * @param string $class the html class for column
3791 * @param object $meta the meta-information about
3792 * this field
3793 * @param array $map the list of relations
3794 * @param array $_url_params the parameters for generate url
3795 * @param boolean $condition_field the column should highlighted
3796 * or not
3797 * @param object|string $transformation_plugin the name of transformation
3798 * function
3799 * @param string $default_function the default transformation
3800 * function
3801 * @param string $transform_options the transformation parameters
3802 * @param array $analyzed_sql_results the analyzed query
3804 * @return string $cell the prepared data cell, html content
3806 * @access private
3808 * @see _getTableBody()
3810 private function _getDataCellForGeometryColumns(
3811 $column, $class, $meta, $map, $_url_params, $condition_field,
3812 $transformation_plugin, $default_function, $transform_options,
3813 $analyzed_sql_results
3815 if (! isset($column) || is_null($column)) {
3816 $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
3817 return $cell;
3820 if ($column == '') {
3821 $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
3822 return $cell;
3825 // Display as [GEOMETRY - (size)]
3826 if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_GEOM) {
3827 $geometry_text = $this->_handleNonPrintableContents(
3828 strtoupper(self::GEOMETRY_FIELD), $column, $transformation_plugin,
3829 $transform_options, $default_function, $meta, $_url_params
3832 $cell = $this->_buildValueDisplay(
3833 $class, $condition_field, $geometry_text
3835 return $cell;
3838 if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_WKT) {
3839 // Prepare in Well Known Text(WKT) format.
3840 $where_comparison = ' = ' . $column;
3842 // Convert to WKT format
3843 $wktval = Util::asWKT($column);
3844 list(
3845 $is_field_truncated,
3846 $wktval,
3847 // skip 3rd param
3848 ) = $this->_getPartialText($wktval);
3850 $cell = $this->_getRowData(
3851 $class, $condition_field, $analyzed_sql_results, $meta, $map,
3852 $wktval, $transformation_plugin, $default_function, '',
3853 $where_comparison, $transform_options,
3854 $is_field_truncated, ''
3856 return $cell;
3859 // Prepare in Well Known Binary (WKB) format.
3861 if ($_SESSION['tmpval']['display_binary']) {
3862 $where_comparison = ' = ' . $column;
3864 $wkbval = substr(bin2hex($column), 8);
3865 list(
3866 $is_field_truncated,
3867 $wkbval,
3868 // skip 3rd param
3869 ) = $this->_getPartialText($wkbval);
3871 $cell = $this->_getRowData(
3872 $class, $condition_field,
3873 $analyzed_sql_results, $meta, $map, $wkbval,
3874 $transformation_plugin, $default_function, '',
3875 $where_comparison, $transform_options,
3876 $is_field_truncated, ''
3878 return $cell;
3881 $wkbval = $this->_handleNonPrintableContents(
3882 self::BINARY_FIELD, $column, $transformation_plugin,
3883 $transform_options, $default_function, $meta,
3884 $_url_params
3887 $cell = $this->_buildValueDisplay(
3888 $class, $condition_field, $wkbval
3891 return $cell;
3893 } // end of the '_getDataCellForGeometryColumns()' function
3897 * Get data cell for non numeric type fields
3899 * @param string $column the relevant column in data row
3900 * @param string $class the html class for column
3901 * @param object $meta the meta-information about
3902 * the field
3903 * @param array $map the list of relations
3904 * @param array $_url_params the parameters for generate
3905 * url
3906 * @param boolean $condition_field the column should highlighted
3907 * or not
3908 * @param object|string $transformation_plugin the name of transformation
3909 * function
3910 * @param string $default_function the default transformation
3911 * function
3912 * @param string $transform_options the transformation parameters
3913 * @param boolean $is_field_truncated is data truncated due to
3914 * LimitChars
3915 * @param array $analyzed_sql_results the analyzed query
3916 * @param integer &$dt_result the link id associated to
3917 * the query which results
3918 * have to be displayed
3919 * @param integer $col_index the column index
3921 * @return string $cell the prepared data cell, html content
3923 * @access private
3925 * @see _getTableBody()
3927 private function _getDataCellForNonNumericColumns(
3928 $column, $class, $meta, $map, $_url_params, $condition_field,
3929 $transformation_plugin, $default_function, $transform_options,
3930 $is_field_truncated, $analyzed_sql_results, &$dt_result, $col_index
3932 $original_length = 0;
3934 $is_analyse = $this->__get('is_analyse');
3935 $field_flags = $GLOBALS['dbi']->fieldFlags($dt_result, $col_index);
3937 $bIsText = gettype($transformation_plugin) === 'object'
3938 && strpos($transformation_plugin->getMIMEtype(), 'Text')
3939 === false;
3941 // disable inline grid editing
3942 // if binary fields are protected
3943 // or transformation plugin is of non text type
3944 // such as image
3945 if ((stristr($field_flags, self::BINARY_FIELD)
3946 && ($GLOBALS['cfg']['ProtectBinary'] === 'all'
3947 || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob'
3948 && !stristr($meta->type, self::BLOB_FIELD))
3949 || ($GLOBALS['cfg']['ProtectBinary'] === 'blob'
3950 && stristr($meta->type, self::BLOB_FIELD))))
3951 || $bIsText
3953 $class = str_replace('grid_edit', '', $class);
3956 if (! isset($column) || is_null($column)) {
3957 $cell = $this->_buildNullDisplay($class, $condition_field, $meta);
3958 return $cell;
3961 if ($column == '') {
3962 $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta);
3963 return $cell;
3966 // Cut all fields to $GLOBALS['cfg']['LimitChars']
3967 // (unless it's a link-type transformation or binary)
3968 if (!(gettype($transformation_plugin) === "object"
3969 && strpos($transformation_plugin->getName(), 'Link') !== false)
3970 && !stristr($field_flags, self::BINARY_FIELD)
3972 list(
3973 $is_field_truncated,
3974 $column,
3975 $original_length
3976 ) = $this->_getPartialText($column);
3979 $formatted = false;
3980 if (isset($meta->_type) && $meta->_type === MYSQLI_TYPE_BIT) {
3982 $column = Util::printableBitValue(
3983 $column, $meta->length
3986 // some results of PROCEDURE ANALYSE() are reported as
3987 // being BINARY but they are quite readable,
3988 // so don't treat them as BINARY
3989 } elseif (stristr($field_flags, self::BINARY_FIELD)
3990 && !(isset($is_analyse) && $is_analyse)
3992 // we show the BINARY or BLOB message and field's size
3993 // (or maybe use a transformation)
3994 $binary_or_blob = self::BLOB_FIELD;
3995 if ($meta->type === self::STRING_FIELD) {
3996 $binary_or_blob = self::BINARY_FIELD;
3998 $column = $this->_handleNonPrintableContents(
3999 $binary_or_blob, $column, $transformation_plugin,
4000 $transform_options, $default_function,
4001 $meta, $_url_params, $is_field_truncated
4003 $class = $this->_addClass(
4004 $class, $condition_field, $meta, '',
4005 $is_field_truncated, $transformation_plugin, $default_function
4007 $result = strip_tags($column);
4008 // disable inline grid editing
4009 // if binary or blob data is not shown
4010 if (stristr($result, $binary_or_blob)) {
4011 $class = str_replace('grid_edit', '', $class);
4013 $formatted = true;
4016 if ($formatted) {
4017 $cell = $this->_buildValueDisplay(
4018 $class, $condition_field, $column
4020 return $cell;
4023 // transform functions may enable no-wrapping:
4024 $function_nowrap = 'applyTransformationNoWrap';
4026 $bool_nowrap = (($default_function != $transformation_plugin)
4027 && function_exists($transformation_plugin->$function_nowrap()))
4028 ? $transformation_plugin->$function_nowrap($transform_options)
4029 : false;
4031 // do not wrap if date field type
4032 $nowrap = (preg_match('@DATE|TIME@i', $meta->type)
4033 || $bool_nowrap) ? ' nowrap' : '';
4035 $where_comparison = ' = \''
4036 . Util::sqlAddSlashes($column)
4037 . '\'';
4039 $cell = $this->_getRowData(
4040 $class, $condition_field,
4041 $analyzed_sql_results, $meta, $map, $column,
4042 $transformation_plugin, $default_function, $nowrap,
4043 $where_comparison, $transform_options,
4044 $is_field_truncated, $original_length
4047 return $cell;
4049 } // end of the '_getDataCellForNonNumericColumns()' function
4052 * Checks the posted options for viewing query results
4053 * and sets appropriate values in the session.
4055 * @todo make maximum remembered queries configurable
4056 * @todo move/split into SQL class!?
4057 * @todo currently this is called twice unnecessary
4058 * @todo ignore LIMIT and ORDER in query!?
4060 * @return void
4062 * @access public
4064 * @see sql.php file
4066 public function setConfigParamsForDisplayTable()
4069 $sql_md5 = md5($this->__get('sql_query'));
4070 $query = array();
4071 if (isset($_SESSION['tmpval']['query'][$sql_md5])) {
4072 $query = $_SESSION['tmpval']['query'][$sql_md5];
4075 $query['sql'] = $this->__get('sql_query');
4077 if (empty($query['repeat_cells'])) {
4078 $query['repeat_cells'] = $GLOBALS['cfg']['RepeatCells'];
4081 // as this is a form value, the type is always string so we cannot
4082 // use PMA_isValid($_REQUEST['session_max_rows'], 'integer')
4083 if (PMA_isValid($_REQUEST['session_max_rows'], 'numeric')) {
4084 $query['max_rows'] = (int)$_REQUEST['session_max_rows'];
4085 unset($_REQUEST['session_max_rows']);
4086 } elseif ($_REQUEST['session_max_rows'] == self::ALL_ROWS) {
4087 $query['max_rows'] = self::ALL_ROWS;
4088 unset($_REQUEST['session_max_rows']);
4089 } elseif (empty($query['max_rows'])) {
4090 $query['max_rows'] = $GLOBALS['cfg']['MaxRows'];
4093 if (PMA_isValid($_REQUEST['pos'], 'numeric')) {
4094 $query['pos'] = $_REQUEST['pos'];
4095 unset($_REQUEST['pos']);
4096 } elseif (empty($query['pos'])) {
4097 $query['pos'] = 0;
4100 if (PMA_isValid(
4101 $_REQUEST['pftext'],
4102 array(
4103 self::DISPLAY_PARTIAL_TEXT, self::DISPLAY_FULL_TEXT
4107 $query['pftext'] = $_REQUEST['pftext'];
4108 unset($_REQUEST['pftext']);
4109 } elseif (empty($query['pftext'])) {
4110 $query['pftext'] = self::DISPLAY_PARTIAL_TEXT;
4113 if (PMA_isValid(
4114 $_REQUEST['relational_display'],
4115 array(
4116 self::RELATIONAL_KEY, self::RELATIONAL_DISPLAY_COLUMN
4120 $query['relational_display'] = $_REQUEST['relational_display'];
4121 unset($_REQUEST['relational_display']);
4122 } elseif (empty($query['relational_display'])) {
4123 // The current session value has priority over a
4124 // change via Settings; this change will be apparent
4125 // starting from the next session
4126 $query['relational_display'] = $GLOBALS['cfg']['RelationalDisplay'];
4129 if (PMA_isValid(
4130 $_REQUEST['geoOption'],
4131 array(
4132 self::GEOMETRY_DISP_WKT, self::GEOMETRY_DISP_WKB,
4133 self::GEOMETRY_DISP_GEOM
4137 $query['geoOption'] = $_REQUEST['geoOption'];
4138 unset($_REQUEST['geoOption']);
4139 } elseif (empty($query['geoOption'])) {
4140 $query['geoOption'] = self::GEOMETRY_DISP_GEOM;
4143 if (isset($_REQUEST['display_binary'])) {
4144 $query['display_binary'] = true;
4145 unset($_REQUEST['display_binary']);
4146 } elseif (isset($_REQUEST['display_options_form'])) {
4147 // we know that the checkbox was unchecked
4148 unset($query['display_binary']);
4149 } elseif (isset($_REQUEST['full_text_button'])) {
4150 // do nothing to keep the value that is there in the session
4151 } else {
4152 // selected by default because some operations like OPTIMIZE TABLE
4153 // and all queries involving functions return "binary" contents,
4154 // according to low-level field flags
4155 $query['display_binary'] = true;
4158 if (isset($_REQUEST['display_blob'])) {
4159 $query['display_blob'] = true;
4160 unset($_REQUEST['display_blob']);
4161 } elseif (isset($_REQUEST['display_options_form'])) {
4162 // we know that the checkbox was unchecked
4163 unset($query['display_blob']);
4166 if (isset($_REQUEST['hide_transformation'])) {
4167 $query['hide_transformation'] = true;
4168 unset($_REQUEST['hide_transformation']);
4169 } elseif (isset($_REQUEST['display_options_form'])) {
4170 // we know that the checkbox was unchecked
4171 unset($query['hide_transformation']);
4174 // move current query to the last position, to be removed last
4175 // so only least executed query will be removed if maximum remembered
4176 // queries limit is reached
4177 unset($_SESSION['tmpval']['query'][$sql_md5]);
4178 $_SESSION['tmpval']['query'][$sql_md5] = $query;
4180 // do not exceed a maximum number of queries to remember
4181 if (count($_SESSION['tmpval']['query']) > 10) {
4182 array_shift($_SESSION['tmpval']['query']);
4183 //echo 'deleting one element ...';
4186 // populate query configuration
4187 $_SESSION['tmpval']['pftext']
4188 = $query['pftext'];
4189 $_SESSION['tmpval']['relational_display']
4190 = $query['relational_display'];
4191 $_SESSION['tmpval']['geoOption']
4192 = $query['geoOption'];
4193 $_SESSION['tmpval']['display_binary'] = isset(
4194 $query['display_binary']
4196 $_SESSION['tmpval']['display_blob'] = isset(
4197 $query['display_blob']
4199 $_SESSION['tmpval']['hide_transformation'] = isset(
4200 $query['hide_transformation']
4202 $_SESSION['tmpval']['pos']
4203 = $query['pos'];
4204 $_SESSION['tmpval']['max_rows']
4205 = $query['max_rows'];
4206 $_SESSION['tmpval']['repeat_cells']
4207 = $query['repeat_cells'];
4211 * Prepare a table of results returned by a SQL query.
4213 * @param integer &$dt_result the link id associated to the query
4214 * which results have to be displayed
4215 * @param array &$displayParts the parts to display
4216 * @param array $analyzed_sql_results analyzed sql results
4217 * @param boolean $is_limited_display With limited operations or not
4219 * @return string $table_html Generated HTML content for resulted table
4221 * @access public
4223 * @see sql.php file
4225 public function getTable(
4226 &$dt_result, &$displayParts, $analyzed_sql_results,
4227 $is_limited_display = false
4231 * The statement this table is built for.
4232 * @var \SqlParser\Statements\SelectStatement
4234 $statement = $analyzed_sql_results['statement'];
4236 $table_html = '';
4237 // Following variable are needed for use in isset/empty or
4238 // use with array indexes/safe use in foreach
4239 $fields_meta = $this->__get('fields_meta');
4240 $showtable = $this->__get('showtable');
4241 $printview = $this->__get('printview');
4243 // why was this called here? (already called from sql.php)
4244 //$this->setConfigParamsForDisplayTable();
4247 * @todo move this to a central place
4248 * @todo for other future table types
4250 $is_innodb = (isset($showtable['Type'])
4251 && $showtable['Type'] == self::TABLE_TYPE_INNO_DB);
4253 if ($is_innodb
4254 && PMA_isJustBrowsing($analyzed_sql_results, true)
4256 // "j u s t b r o w s i n g"
4257 $pre_count = '~';
4258 $after_count = Util::showHint(
4259 PMA_sanitize(
4260 __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc].')
4263 } else {
4264 $pre_count = '';
4265 $after_count = '';
4268 // 1. ----- Prepares the work -----
4270 // 1.1 Gets the information about which functionalities should be
4271 // displayed
4273 list(
4274 $displayParts,
4275 $total
4276 ) = $this->_setDisplayPartsAndTotal($displayParts);
4278 // 1.2 Defines offsets for the next and previous pages
4279 if ($displayParts['nav_bar'] == '1') {
4280 list($pos_next, $pos_prev) = $this->_getOffsets();
4281 } // end if
4283 // 1.3 Extract sorting expressions.
4284 // we need $sort_expression and $sort_expression_nodirection
4285 // even if there are many table references
4286 $sort_expression = array();
4287 $sort_expression_nodirection = array();
4288 $sort_direction = array();
4290 if (!empty($statement->order)) {
4291 foreach ($statement->order as $o) {
4292 $sort_expression[] = $o->expr->expr . ' ' . $o->type;
4293 $sort_expression_nodirection[] = $o->expr->expr;
4294 $sort_direction[] = $o->type;
4296 } else {
4297 $sort_expression[] = '';
4298 $sort_expression_nodirection[] = '';
4299 $sort_direction[] = '';
4302 $number_of_columns = count($sort_expression_nodirection);
4304 // 1.4 Prepares display of first and last value of the sorted column
4305 $sorted_column_message = '';
4306 for ( $i = 0; $i < $number_of_columns; $i++ ) {
4307 $sorted_column_message .= $this->_getSortedColumnMessage(
4308 $dt_result, $sort_expression_nodirection[$i]
4312 // 2. ----- Prepare to display the top of the page -----
4314 // 2.1 Prepares a messages with position information
4315 if (($displayParts['nav_bar'] == '1') && isset($pos_next)) {
4317 $message = $this->_setMessageInformation(
4318 $sorted_column_message,
4319 $analyzed_sql_results,
4320 $total,
4321 $pos_next,
4322 $pre_count,
4323 $after_count
4326 $table_html .= Util::getMessage(
4327 $message, $this->__get('sql_query'), 'success'
4330 } elseif ((!isset($printview) || ($printview != '1')) && !$is_limited_display) {
4332 $table_html .= Util::getMessage(
4333 __('Your SQL query has been executed successfully.'),
4334 $this->__get('sql_query'), 'success'
4338 // 2.3 Prepare the navigation bars
4339 if (!mb_strlen($this->__get('table'))) {
4341 if ($analyzed_sql_results['querytype'] == 'SELECT') {
4342 // table does not always contain a real table name,
4343 // for example in MySQL 5.0.x, the query SHOW STATUS
4344 // returns STATUS as a table name
4345 $this->__set('table', $fields_meta[0]->table);
4346 } else {
4347 $this->__set('table', '');
4352 // can the result be sorted?
4353 if ($displayParts['sort_lnk'] == '1') {
4355 // At this point, $sort_expression is an array but we only verify
4356 // the first element in case we could find that the table is
4357 // sorted by one of the choices listed in the
4358 // "Sort by key" drop-down
4359 list($unsorted_sql_query, $sort_by_key_html)
4360 = $this->_getUnsortedSqlAndSortByKeyDropDown(
4361 $analyzed_sql_results, $sort_expression[0]
4364 } else {
4365 $sort_by_key_html = $unsorted_sql_query = '';
4368 if (($displayParts['nav_bar'] == '1') && (empty($statement->limit))) {
4369 $table_html .= $this->_getPlacedTableNavigations(
4370 $pos_next, $pos_prev, self::PLACE_TOP_DIRECTION_DROPDOWN,
4371 $is_innodb, $sort_by_key_html
4375 // 2b ----- Get field references from Database -----
4376 // (see the 'relation' configuration variable)
4378 // initialize map
4379 $map = array();
4381 $target = array();
4382 if (!empty($statement->from)) {
4383 foreach ($statement->from as $field) {
4384 if (!empty($field->table)) {
4385 $target[] = $field->table;
4390 if (mb_strlen($this->__get('table'))) {
4391 // This method set the values for $map array
4392 $this->_setParamForLinkForeignKeyRelatedTables($map);
4394 // Coming from 'Distinct values' action of structure page
4395 // We manipulate relations mechanism to show a link to related rows.
4396 if ($this->__get('is_browse_distinct')) {
4397 $map[$fields_meta[1]->name] = array(
4398 $this->__get('table'),
4399 $fields_meta[1]->name,
4401 $this->__get('db')
4404 } // end if
4405 // end 2b
4407 // 3. ----- Prepare the results table -----
4408 if ($is_limited_display) {
4409 $table_html .= "<br>";
4412 $table_html .= $this->_getTableHeaders(
4413 $displayParts,
4414 $analyzed_sql_results,
4415 $unsorted_sql_query,
4416 $sort_expression,
4417 $sort_expression_nodirection,
4418 $sort_direction,
4419 $is_limited_display
4422 $table_html .= '<tbody>' . "\n";
4424 $table_html .= $this->_getTableBody(
4425 $dt_result,
4426 $displayParts,
4427 $map,
4428 $analyzed_sql_results,
4429 $is_limited_display
4432 $this->__set('display_params', null);
4434 $table_html .= '</tbody>' . "\n" . '</table>';
4436 // 4. ----- Prepares the link for multi-fields edit and delete
4438 if ($displayParts['del_lnk'] == self::DELETE_ROW
4439 && $displayParts['del_lnk'] != self::KILL_PROCESS
4442 $table_html .= $this->_getMultiRowOperationLinks(
4443 $dt_result,
4444 $analyzed_sql_results,
4445 $displayParts['del_lnk']
4450 // 5. ----- Get the navigation bar at the bottom if required -----
4451 if (($displayParts['nav_bar'] == '1') && empty($statement->limit)) {
4452 $table_html .= $this->_getPlacedTableNavigations(
4453 $pos_next, $pos_prev, self::PLACE_BOTTOM_DIRECTION_DROPDOWN,
4454 $is_innodb, $sort_by_key_html
4456 } elseif (! isset($printview) || ($printview != '1')) {
4457 $table_html .= "\n" . '<br /><br />' . "\n";
4460 // 6. ----- Prepare "Query results operations"
4461 if ((! isset($printview) || ($printview != '1')) && ! $is_limited_display) {
4462 $table_html .= $this->_getResultsOperations(
4463 $displayParts, $analyzed_sql_results
4467 return $table_html;
4469 } // end of the 'getTable()' function
4473 * Get offsets for next page and previous page
4475 * @return array array with two elements - $pos_next, $pos_prev
4477 * @access private
4479 * @see getTable()
4481 private function _getOffsets()
4484 if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) {
4485 $pos_next = 0;
4486 $pos_prev = 0;
4487 } else {
4489 $pos_next = $_SESSION['tmpval']['pos']
4490 + $_SESSION['tmpval']['max_rows'];
4492 $pos_prev = $_SESSION['tmpval']['pos']
4493 - $_SESSION['tmpval']['max_rows'];
4495 if ($pos_prev < 0) {
4496 $pos_prev = 0;
4500 return array($pos_next, $pos_prev);
4502 } // end of the '_getOffsets()' function
4506 * Prepare sorted column message
4508 * @param integer &$dt_result the link id associated to the
4509 * query which results have to
4510 * be displayed
4511 * @param string $sort_expression_nodirection sort expression without direction
4513 * @return string html content
4514 * null if not found sorted column
4516 * @access private
4518 * @see getTable()
4520 private function _getSortedColumnMessage(
4521 &$dt_result, $sort_expression_nodirection
4524 $fields_meta = $this->__get('fields_meta'); // To use array indexes
4526 if (empty($sort_expression_nodirection)) {
4527 return null;
4530 if (mb_strpos($sort_expression_nodirection, '.') === false) {
4531 $sort_table = $this->__get('table');
4532 $sort_column = $sort_expression_nodirection;
4533 } else {
4534 list($sort_table, $sort_column)
4535 = explode('.', $sort_expression_nodirection);
4538 $sort_table = Util::unQuote($sort_table);
4539 $sort_column = Util::unQuote($sort_column);
4541 // find the sorted column index in row result
4542 // (this might be a multi-table query)
4543 $sorted_column_index = false;
4545 foreach ($fields_meta as $key => $meta) {
4546 if (($meta->table == $sort_table) && ($meta->name == $sort_column)) {
4547 $sorted_column_index = $key;
4548 break;
4552 if ($sorted_column_index === false) {
4553 return null;
4556 // fetch first row of the result set
4557 $row = $GLOBALS['dbi']->fetchRow($dt_result);
4559 // initializing default arguments
4560 $default_function = 'PMA_mimeDefaultFunction';
4561 $transformation_plugin = $default_function;
4562 $transform_options = array();
4564 // check for non printable sorted row data
4565 $meta = $fields_meta[$sorted_column_index];
4567 if (stristr($meta->type, self::BLOB_FIELD)
4568 || ($meta->type == self::GEOMETRY_FIELD)
4571 $column_for_first_row = $this->_handleNonPrintableContents(
4572 $meta->type, $row[$sorted_column_index],
4573 $transformation_plugin, $transform_options,
4574 $default_function, $meta
4577 } else {
4578 $column_for_first_row = $row[$sorted_column_index];
4581 $column_for_first_row = mb_strtoupper(
4582 mb_substr(
4583 $column_for_first_row, 0, $GLOBALS['cfg']['LimitChars']
4584 ) . '...'
4587 // fetch last row of the result set
4588 $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1);
4589 $row = $GLOBALS['dbi']->fetchRow($dt_result);
4591 // check for non printable sorted row data
4592 $meta = $fields_meta[$sorted_column_index];
4593 if (stristr($meta->type, self::BLOB_FIELD)
4594 || ($meta->type == self::GEOMETRY_FIELD)
4597 $column_for_last_row = $this->_handleNonPrintableContents(
4598 $meta->type, $row[$sorted_column_index],
4599 $transformation_plugin, $transform_options,
4600 $default_function, $meta
4603 } else {
4604 $column_for_last_row = $row[$sorted_column_index];
4607 $column_for_last_row = mb_strtoupper(
4608 mb_substr(
4609 $column_for_last_row, 0, $GLOBALS['cfg']['LimitChars']
4610 ) . '...'
4613 // reset to first row for the loop in _getTableBody()
4614 $GLOBALS['dbi']->dataSeek($dt_result, 0);
4616 // we could also use here $sort_expression_nodirection
4617 return ' [' . htmlspecialchars($sort_column)
4618 . ': <strong>' . htmlspecialchars($column_for_first_row) . ' - '
4619 . htmlspecialchars($column_for_last_row) . '</strong>]';
4620 } // end of the '_getSortedColumnMessage()' function
4624 * Set the content that needs to be shown in message
4626 * @param string $sorted_column_message the message for sorted column
4627 * @param array $analyzed_sql_results the analyzed query
4628 * @param integer $total the total number of rows returned by
4629 * the SQL query without any
4630 * programmatically appended LIMIT clause
4631 * @param integer $pos_next the offset for next page
4632 * @param string $pre_count the string renders before row count
4633 * @param string $after_count the string renders after row count
4635 * @return Message $message an object of Message
4637 * @access private
4639 * @see getTable()
4641 private function _setMessageInformation(
4642 $sorted_column_message, $analyzed_sql_results, $total,
4643 $pos_next, $pre_count, $after_count
4646 $unlim_num_rows = $this->__get('unlim_num_rows'); // To use in isset()
4648 if (!empty($analyzed_sql_results['statement']->limit)) {
4650 $first_shown_rec = $analyzed_sql_results['statement']->limit->offset;
4651 $row_count = $analyzed_sql_results['statement']->limit->rowCount;
4653 if ($row_count < $total) {
4654 $last_shown_rec = $first_shown_rec + $row_count - 1;
4655 } else {
4656 $last_shown_rec = $first_shown_rec + $total - 1;
4659 } elseif (($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS)
4660 || ($pos_next > $total)
4663 $first_shown_rec = $_SESSION['tmpval']['pos'];
4664 $last_shown_rec = $total - 1;
4666 } else {
4668 $first_shown_rec = $_SESSION['tmpval']['pos'];
4669 $last_shown_rec = $pos_next - 1;
4673 $table = new Table($this->__get('table'), $this->__get('db'));
4674 if ($table->isView()
4675 && ($total == $GLOBALS['cfg']['MaxExactCountViews'])
4678 $message = Message::notice(
4680 'This view has at least this number of rows. '
4681 . 'Please refer to %sdocumentation%s.'
4685 $message->addParam('[doc@cfg_MaxExactCount]');
4686 $message->addParam('[/doc]');
4687 $message_view_warning = Util::showHint($message);
4689 } else {
4690 $message_view_warning = false;
4693 $message = Message::success(__('Showing rows %1s - %2s'));
4694 $message->addParam($first_shown_rec);
4696 if ($message_view_warning !== false) {
4697 $message->addParam('... ' . $message_view_warning, false);
4698 } else {
4699 $message->addParam($last_shown_rec);
4702 $message->addMessage('(');
4704 if ($message_view_warning === false) {
4706 if (isset($unlim_num_rows) && ($unlim_num_rows != $total)) {
4707 $message_total = Message::notice(
4708 $pre_count . __('%1$d total, %2$d in query')
4710 $message_total->addParam($total);
4711 $message_total->addParam($unlim_num_rows);
4712 } else {
4713 $message_total = Message::notice($pre_count . __('%d total'));
4714 $message_total->addParam($total);
4717 if (!empty($after_count)) {
4718 $message_total->addMessage($after_count);
4720 $message->addMessage($message_total, '');
4722 $message->addMessage(', ', '');
4725 $message_qt = Message::notice(__('Query took %01.4f seconds.') . ')');
4726 $message_qt->addParam($this->__get('querytime'));
4728 $message->addMessage($message_qt, '');
4729 if (! is_null($sorted_column_message)) {
4730 $message->addMessage($sorted_column_message, '');
4733 return $message;
4735 } // end of the '_setMessageInformation()' function
4739 * Set the value of $map array for linking foreign key related tables
4741 * @param array &$map the list of relations
4743 * @return void
4745 * @access private
4747 * @see getTable()
4749 private function _setParamForLinkForeignKeyRelatedTables(&$map)
4752 // To be able to later display a link to the related table,
4753 // we verify both types of relations: either those that are
4754 // native foreign keys or those defined in the phpMyAdmin
4755 // configuration storage. If no PMA storage, we won't be able
4756 // to use the "column to display" notion (for example show
4757 // the name related to a numeric id).
4758 $exist_rel = PMA_getForeigners(
4759 $this->__get('db'), $this->__get('table'), '', self::POSITION_BOTH
4762 if (! empty($exist_rel)) {
4764 foreach ($exist_rel as $master_field => $rel) {
4765 if ($master_field != 'foreign_keys_data') {
4766 $display_field = PMA_getDisplayField(
4767 $rel['foreign_db'], $rel['foreign_table']
4769 $map[$master_field] = array(
4770 $rel['foreign_table'],
4771 $rel['foreign_field'],
4772 $display_field,
4773 $rel['foreign_db']
4775 } else {
4776 foreach ($rel as $key => $one_key) {
4777 foreach ($one_key['index_list'] as $index => $one_field) {
4778 $display_field = PMA_getDisplayField(
4779 isset($one_key['ref_db_name'])
4780 ? $one_key['ref_db_name']
4781 : $GLOBALS['db'],
4782 $one_key['ref_table_name']
4785 $map[$one_field] = array(
4786 $one_key['ref_table_name'],
4787 $one_key['ref_index_list'][$index],
4788 $display_field,
4789 isset($one_key['ref_db_name'])
4790 ? $one_key['ref_db_name']
4791 : $GLOBALS['db']
4796 } // end while
4797 } // end if
4799 } // end of the '_setParamForLinkForeignKeyRelatedTables()' function
4803 * Prepare multi field edit/delete links
4805 * @param integer &$dt_result the link id associated to the query which
4806 * results have to be displayed
4807 * @param array $analyzed_sql_results analyzed sql results
4808 * @param string $del_link the display element - 'del_link'
4810 * @return string $links_html html content
4812 * @access private
4814 * @see getTable()
4816 private function _getMultiRowOperationLinks(
4817 &$dt_result, $analyzed_sql_results, $del_link
4820 $links_html = '<div class="print_ignore" >';
4821 $url_query = $this->__get('url_query');
4822 $delete_text = ($del_link == self::DELETE_ROW) ? __('Delete') : __('Kill');
4824 $links_html .= '<img class="selectallarrow" width="38" height="22"'
4825 . ' src="' . $this->__get('pma_theme_image') . 'arrow_'
4826 . $this->__get('text_dir') . '.png' . '"'
4827 . ' alt="' . __('With selected:') . '" />';
4829 $links_html .= '<input type="checkbox" '
4830 . 'id="resultsForm_' . $this->__get('unique_id') . '_checkall" '
4831 . 'class="checkall_box" title="' . __('Check all') . '" /> '
4832 . '<label for="resultsForm_' . $this->__get('unique_id') . '_checkall">'
4833 . __('Check all') . '</label> '
4834 . '<i style="margin-left: 2em">' . __('With selected:') . '</i>' . "\n";
4836 $links_html .= Util::getButtonOrImage(
4837 'submit_mult', 'mult_submit', 'submit_mult_change',
4838 __('Edit'), 'b_edit.png', 'edit'
4841 $links_html .= Util::getButtonOrImage(
4842 'submit_mult', 'mult_submit', 'submit_mult_copy',
4843 __('Copy'), 'b_insrow.png', 'copy'
4846 $links_html .= Util::getButtonOrImage(
4847 'submit_mult', 'mult_submit', 'submit_mult_delete',
4848 $delete_text, 'b_drop.png', 'delete'
4851 if ($analyzed_sql_results['querytype'] == 'SELECT') {
4852 $links_html .= Util::getButtonOrImage(
4853 'submit_mult', 'mult_submit', 'submit_mult_export',
4854 __('Export'), 'b_tblexport.png', 'export'
4858 $links_html .= "</div>\n";
4860 $links_html .= '<input type="hidden" name="sql_query"'
4861 . ' value="' . htmlspecialchars($this->__get('sql_query')) . '" />'
4862 . "\n";
4864 if (! empty($url_query)) {
4865 $links_html .= '<input type="hidden" name="url_query"'
4866 . ' value="' . $url_query . '" />' . "\n";
4869 // fetch last row of the result set
4870 $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1);
4871 $row = $GLOBALS['dbi']->fetchRow($dt_result);
4873 // $clause_is_unique is needed by getTable() to generate the proper param
4874 // in the multi-edit and multi-delete form
4875 list($where_clause, $clause_is_unique, $condition_array)
4876 = Util::getUniqueCondition(
4877 $dt_result, // handle
4878 $this->__get('fields_cnt'), // fields_cnt
4879 $this->__get('fields_meta'), // fields_meta
4880 $row, // row
4881 false, // force_unique
4882 false, // restrict_to_table
4883 $analyzed_sql_results // analyzed_sql_results
4885 unset($where_clause, $condition_array);
4887 // reset to first row for the loop in _getTableBody()
4888 $GLOBALS['dbi']->dataSeek($dt_result, 0);
4890 $links_html .= '<input type="hidden" name="clause_is_unique"'
4891 . ' value="' . $clause_is_unique . '" />' . "\n";
4893 $links_html .= '</form>' . "\n";
4895 return $links_html;
4897 } // end of the '_getMultiRowOperationLinks()' function
4901 * Prepare table navigation bar at the top or bottom
4903 * @param integer $pos_next the offset for the "next" page
4904 * @param integer $pos_prev the offset for the "previous" page
4905 * @param string $place the place to show navigation
4906 * @param boolean $is_innodb whether its InnoDB or not
4907 * @param string $sort_by_key_html the sort by key dialog
4909 * @return string html content of navigation bar
4911 * @access private
4913 * @see _getTable()
4915 private function _getPlacedTableNavigations(
4916 $pos_next, $pos_prev, $place, $is_innodb, $sort_by_key_html
4919 $navigation_html = '';
4921 if ($place == self::PLACE_BOTTOM_DIRECTION_DROPDOWN) {
4922 $navigation_html .= '<br />' . "\n";
4925 $navigation_html .= $this->_getTableNavigation(
4926 $pos_next, $pos_prev, $is_innodb, $sort_by_key_html
4929 if ($place == self::PLACE_TOP_DIRECTION_DROPDOWN) {
4930 $navigation_html .= "\n";
4933 return $navigation_html;
4935 } // end of the '_getPlacedTableNavigations()' function
4938 * Generates HTML to display the Create view in span tag
4940 * @param array $analyzed_sql_results analyzed sql results
4941 * @param string $url_query String with URL Parameters
4943 * @return string
4945 * @access private
4947 * @see _getResultsOperations()
4949 private function _getLinkForCreateView($analyzed_sql_results, $url_query)
4951 $results_operations_html = '';
4952 if (empty($analyzed_sql_results['procedure'])) {
4954 $ajax_class = ' ajax';
4956 $results_operations_html .= '<span>'
4957 . Util::linkOrButton(
4958 'view_create.php' . $url_query,
4959 Util::getIcon(
4960 'b_view_add.png', __('Create view'), true
4962 array('class' => 'create_view' . $ajax_class), true, true, ''
4964 . '</span>' . "\n";
4966 return $results_operations_html;
4971 * Calls the _getResultsOperations with $only_view as true
4973 * @param array $analyzed_sql_results analyzed sql results
4975 * @return string
4977 * @access public
4980 public function getCreateViewQueryResultOp($analyzed_sql_results)
4983 $results_operations_html = '';
4984 //calling to _getResultOperations with a fake $displayParts
4985 //and setting only_view parameter to be true to generate just view
4986 $results_operations_html .= $this->_getResultsOperations(
4987 array(),
4988 $analyzed_sql_results,
4989 true
4991 return $results_operations_html;
4995 * Get copy to clipboard links for results operations
4997 * @return string $html
4999 * @access private
5001 private function _getCopytoclipboardLinks()
5003 $html = Util::linkOrButton(
5004 '#',
5005 Util::getIcon(
5006 'b_insrow.png', __('Copy to clipboard'), true
5008 array('id' => 'copyToClipBoard'),
5009 true,
5010 true,
5011 'copy_to_clip_board'
5014 return $html;
5018 * Get printview links for results operations
5020 * @return string $html
5022 * @access private
5024 private function _getPrintviewLinks()
5026 $html = Util::linkOrButton(
5027 '#',
5028 Util::getIcon(
5029 'b_print.png', __('Print'), true
5031 array('id' => 'printView'),
5032 true,
5033 true,
5034 'print_view'
5037 return $html;
5041 * Get operations that are available on results.
5043 * @param array $displayParts the parts to display
5044 * @param array $analyzed_sql_results analyzed sql results
5045 * @param boolean $only_view Whether to show only view
5047 * @return string $results_operations_html html content
5049 * @access private
5051 * @see getTable()
5053 private function _getResultsOperations(
5054 $displayParts, $analyzed_sql_results, $only_view = false
5056 global $printview;
5058 $results_operations_html = '';
5059 $fields_meta = $this->__get('fields_meta'); // To safe use in foreach
5060 $header_shown = false;
5061 $header = '<fieldset class="print_ignore" ><legend>'
5062 . __('Query results operations') . '</legend>';
5064 $_url_params = array(
5065 'db' => $this->__get('db'),
5066 'table' => $this->__get('table'),
5067 'printview' => '1',
5068 'sql_query' => $this->__get('sql_query'),
5070 $url_query = PMA_URL_getCommon($_url_params);
5072 if (!$header_shown) {
5073 $results_operations_html .= $header;
5074 $header_shown = true;
5076 // if empty result set was produced we need to
5077 // show only view and not other options
5078 if ($only_view) {
5079 $results_operations_html .= $this->_getLinkForCreateView(
5080 $analyzed_sql_results, $url_query
5083 if ($header_shown) {
5084 $results_operations_html .= '</fieldset><br />';
5086 return $results_operations_html;
5089 // Displays "printable view" link if required
5090 if ($displayParts['pview_lnk'] == '1') {
5091 $results_operations_html .= $this->_getPrintviewLinks();
5092 $results_operations_html .= $this->_getCopytoclipboardLinks();
5093 } // end displays "printable view"
5095 // Export link
5096 // (the url_query has extra parameters that won't be used to export)
5097 // (the single_table parameter is used in PMA_getExportDisplay()
5098 // to hide the SQL and the structure export dialogs)
5099 // If the parser found a PROCEDURE clause
5100 // (most probably PROCEDURE ANALYSE()) it makes no sense to
5101 // display the Export link).
5102 if (($analyzed_sql_results['querytype'] == self::QUERY_TYPE_SELECT)
5103 && ! isset($printview)
5104 && empty($analyzed_sql_results['procedure'])
5107 if (count($analyzed_sql_results['select_tables']) == 1) {
5108 $_url_params['single_table'] = 'true';
5111 if (! $header_shown) {
5112 $results_operations_html .= $header;
5113 $header_shown = true;
5116 $_url_params['unlim_num_rows'] = $this->__get('unlim_num_rows');
5119 * At this point we don't know the table name; this can happen
5120 * for example with a query like
5121 * SELECT bike_code FROM (SELECT bike_code FROM bikes) tmp
5122 * As a workaround we set in the table parameter the name of the
5123 * first table of this database, so that tbl_export.php and
5124 * the script it calls do not fail
5126 if (empty($_url_params['table']) && ! empty($_url_params['db'])) {
5127 $_url_params['table'] = $GLOBALS['dbi']->fetchValue("SHOW TABLES");
5128 /* No result (probably no database selected) */
5129 if ($_url_params['table'] === false) {
5130 unset($_url_params['table']);
5134 $results_operations_html .= Util::linkOrButton(
5135 'tbl_export.php' . PMA_URL_getCommon($_url_params),
5136 Util::getIcon(
5137 'b_tblexport.png', __('Export'), true
5140 true,
5141 true,
5144 . "\n";
5146 // prepare chart
5147 $results_operations_html .= Util::linkOrButton(
5148 'tbl_chart.php' . PMA_URL_getCommon($_url_params),
5149 Util::getIcon(
5150 'b_chart.png', __('Display chart'), true
5153 true,
5154 true,
5157 . "\n";
5159 // prepare GIS chart
5160 $geometry_found = false;
5161 // If at least one geometry field is found
5162 foreach ($fields_meta as $meta) {
5163 if ($meta->type == self::GEOMETRY_FIELD) {
5164 $geometry_found = true;
5165 break;
5169 if ($geometry_found) {
5170 $results_operations_html
5171 .= Util::linkOrButton(
5172 'tbl_gis_visualization.php'
5173 . PMA_URL_getCommon($_url_params),
5174 Util::getIcon(
5175 'b_globe.gif', __('Visualize GIS data'), true
5178 true,
5179 true,
5182 . "\n";
5186 // CREATE VIEW
5189 * @todo detect privileges to create a view
5190 * (but see 2006-01-19 note in display_create_table.lib.php,
5191 * I think we cannot detect db-specific privileges reliably)
5192 * Note: we don't display a Create view link if we found a PROCEDURE clause
5194 if (!$header_shown) {
5195 $results_operations_html .= $header;
5196 $header_shown = true;
5199 $results_operations_html .= $this->_getLinkForCreateView(
5200 $analyzed_sql_results, $url_query
5203 if ($header_shown) {
5204 $results_operations_html .= '</fieldset><br />';
5207 return $results_operations_html;
5209 } // end of the '_getResultsOperations()' function
5213 * Verifies what to do with non-printable contents (binary or BLOB)
5214 * in Browse mode.
5216 * @param string $category BLOB|BINARY|GEOMETRY
5217 * @param string $content the binary content
5218 * @param mixed $transformation_plugin transformation plugin.
5219 * Can also be the default function:
5220 * PMA_mimeDefaultFunction
5221 * @param string $transform_options transformation parameters
5222 * @param string $default_function default transformation function
5223 * @param object $meta the meta-information about the field
5224 * @param array $url_params parameters that should go to the
5225 * download link
5226 * @param boolean &$is_truncated the result is truncated or not
5228 * @return mixed string or float
5230 * @access private
5232 * @see _getDataCellForGeometryColumns(),
5233 * _getDataCellForNonNumericColumns(),
5234 * _getSortedColumnMessage()
5236 private function _handleNonPrintableContents(
5237 $category, $content, $transformation_plugin, $transform_options,
5238 $default_function, $meta, $url_params = array(), &$is_truncated = null
5241 $is_truncated = false;
5242 $result = '[' . $category;
5244 if (isset($content)) {
5246 $size = mb_strlen($content, '8bit');
5247 $display_size = Util::formatByteDown($size, 3, 1);
5248 $result .= ' - ' . $display_size[0] . ' ' . $display_size[1];
5250 } else {
5252 $result .= ' - NULL';
5253 $size = 0;
5257 $result .= ']';
5259 // if we want to use a text transformation on a BLOB column
5260 if (gettype($transformation_plugin) === "object") {
5261 $posMimeOctetstream = strpos(
5262 $transformation_plugin->getMIMESubtype(),
5263 'Octetstream'
5265 $posMimeText = strpos($transformation_plugin->getMIMEtype(), 'Text');
5266 if ($posMimeOctetstream
5267 || $posMimeText !== false
5269 // Applying Transformations on hex string of binary data
5270 // seems more appropriate
5271 $result = pack("H*", bin2hex($content));
5275 if ($size <= 0) {
5276 return($result);
5279 if ($default_function != $transformation_plugin) {
5280 $result = $transformation_plugin->applyTransformation(
5281 $result,
5282 $transform_options,
5283 $meta
5285 return($result);
5288 $result = $default_function($result, array(), $meta);
5289 if (($_SESSION['tmpval']['display_binary']
5290 && $meta->type === self::STRING_FIELD)
5291 || ($_SESSION['tmpval']['display_blob']
5292 && stristr($meta->type, self::BLOB_FIELD))
5294 // in this case, restart from the original $content
5295 if (mb_check_encoding($content, 'utf-8')
5296 && !preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', $content)
5298 // show as text if it's valid utf-8
5299 $result = htmlspecialchars($content);
5300 } else {
5301 $result = '0x' . bin2hex($content);
5303 list(
5304 $is_truncated,
5305 $result,
5306 // skip 3rd param
5307 ) = $this->_getPartialText($result);
5310 /* Create link to download */
5312 // in PHP < 5.5, empty() only checks variables
5313 $tmpdb = $this->__get('db');
5314 if (count($url_params) > 0
5315 && (!empty($tmpdb) && !empty($meta->orgtable))
5317 $result = '<a href="tbl_get_field.php'
5318 . PMA_URL_getCommon($url_params)
5319 . '" class="disableAjax">'
5320 . $result . '</a>';
5323 return($result);
5325 } // end of the '_handleNonPrintableContents()' function
5329 * Retrieves the associated foreign key info for a data cell
5331 * @param array $map the list of relations
5332 * @param object $meta the meta-information about the field
5333 * @param string $where_comparison data for the where clause
5335 * @return string formatted data
5337 * @access private
5340 private function _getFromForeign($map, $meta, $where_comparison)
5342 $dispsql = 'SELECT '
5343 . Util::backquote($map[$meta->name][2])
5344 . ' FROM '
5345 . Util::backquote($map[$meta->name][3])
5346 . '.'
5347 . Util::backquote($map[$meta->name][0])
5348 . ' WHERE '
5349 . Util::backquote($map[$meta->name][1])
5350 . $where_comparison;
5352 $dispresult = $GLOBALS['dbi']->tryQuery(
5353 $dispsql,
5354 null,
5355 DatabaseInterface::QUERY_STORE
5358 if ($dispresult && $GLOBALS['dbi']->numRows($dispresult) > 0) {
5359 list($dispval) = $GLOBALS['dbi']->fetchRow($dispresult, 0);
5360 } else {
5361 $dispval = __('Link not found!');
5364 $GLOBALS['dbi']->freeResult($dispresult);
5366 return $dispval;
5370 * Prepares the displayable content of a data cell in Browse mode,
5371 * taking into account foreign key description field and transformations
5373 * @param string $class css classes for the td element
5374 * @param bool $condition_field whether the column is a part of
5375 * the where clause
5376 * @param array $analyzed_sql_results the analyzed query
5377 * @param object $meta the meta-information about the
5378 * field
5379 * @param array $map the list of relations
5380 * @param string $data data
5381 * @param object|string $transformation_plugin transformation plugin.
5382 * Can also be the default function:
5383 * PMA_mimeDefaultFunction
5384 * @param string $default_function default function
5385 * @param string $nowrap 'nowrap' if the content should
5386 * not be wrapped
5387 * @param string $where_comparison data for the where clause
5388 * @param array $transform_options options for transformation
5389 * @param bool $is_field_truncated whether the field is truncated
5390 * @param string $original_length of a truncated column, or ''
5392 * @return string formatted data
5394 * @access private
5396 * @see _getDataCellForNumericColumns(), _getDataCellForGeometryColumns(),
5397 * _getDataCellForNonNumericColumns(),
5400 private function _getRowData(
5401 $class, $condition_field, $analyzed_sql_results, $meta, $map, $data,
5402 $transformation_plugin, $default_function, $nowrap, $where_comparison,
5403 $transform_options, $is_field_truncated, $original_length=''
5405 $relational_display = $_SESSION['tmpval']['relational_display'];
5406 $printview = $this->__get('printview');
5407 $decimals = isset($meta->decimals) ? $meta->decimals : '-1';
5408 $result = '<td data-decimals="' . $decimals . '"'
5409 . ' data-type="' . $meta->type . '"';
5411 if (! empty($original_length)) {
5412 // cannot use data-original-length
5413 $result .= ' data-originallength="' . $original_length . '"';
5416 $result .= ' class="'
5417 . $this->_addClass(
5418 $class, $condition_field, $meta, $nowrap,
5419 $is_field_truncated, $transformation_plugin, $default_function
5421 . '">';
5423 if (!empty($analyzed_sql_results['statement']->expr)) {
5424 foreach ($analyzed_sql_results['statement']->expr as $expr) {
5425 if ((empty($expr->alias)) || (empty($expr->column))) {
5426 continue;
5428 if (strcasecmp($meta->name, $expr->alias) == 0) {
5429 $meta->name = $expr->column;
5434 if (isset($map[$meta->name])) {
5436 // Field to display from the foreign table?
5437 if (isset($map[$meta->name][2])
5438 && mb_strlen($map[$meta->name][2])
5440 $dispval = $this->_getFromForeign(
5441 $map, $meta, $where_comparison
5443 } else {
5444 $dispval = '';
5445 } // end if... else...
5447 if (isset($printview) && ($printview == '1')) {
5449 $result .= ($transformation_plugin != $default_function
5450 ? $transformation_plugin->applyTransformation(
5451 $data,
5452 $transform_options,
5453 $meta
5455 : $default_function($data)
5457 . ' <code>[-&gt;' . $dispval . ']</code>';
5459 } else {
5461 if ($relational_display == self::RELATIONAL_KEY) {
5463 // user chose "relational key" in the display options, so
5464 // the title contains the display field
5465 $title = (! empty($dispval))
5466 ? ' title="' . htmlspecialchars($dispval) . '"'
5467 : '';
5469 } else {
5470 $title = ' title="' . htmlspecialchars($data) . '"';
5473 $_url_params = array(
5474 'db' => $map[$meta->name][3],
5475 'table' => $map[$meta->name][0],
5476 'pos' => '0',
5477 'sql_query' => 'SELECT * FROM '
5478 . Util::backquote($map[$meta->name][3]) . '.'
5479 . Util::backquote($map[$meta->name][0])
5480 . ' WHERE '
5481 . Util::backquote($map[$meta->name][1])
5482 . $where_comparison,
5485 $result .= '<a class="ajax" href="sql.php'
5486 . PMA_URL_getCommon($_url_params)
5487 . '"' . $title . '>';
5489 if ($transformation_plugin != $default_function) {
5490 // always apply a transformation on the real data,
5491 // not on the display field
5492 $result .= $transformation_plugin->applyTransformation(
5493 $data,
5494 $transform_options,
5495 $meta
5497 } else {
5499 if ($relational_display == self::RELATIONAL_DISPLAY_COLUMN
5500 && ! empty($map[$meta->name][2])
5502 // user chose "relational display field" in the
5503 // display options, so show display field in the cell
5504 $result .= $default_function($dispval);
5505 } else {
5506 // otherwise display data in the cell
5507 $result .= $default_function($data);
5511 $result .= '</a>';
5514 } else {
5515 $result .= ($transformation_plugin != $default_function
5516 ? $transformation_plugin->applyTransformation(
5517 $data,
5518 $transform_options,
5519 $meta
5521 : $default_function($data)
5525 $result .= '</td>' . "\n";
5527 return $result;
5529 } // end of the '_getRowData()' function
5533 * Prepares a checkbox for multi-row submits
5535 * @param string $del_url delete url
5536 * @param array $displayParts array with explicit indexes for all
5537 * the display elements
5538 * @param string $row_no the row number
5539 * @param string $where_clause_html url encoded where clause
5540 * @param array $condition_array array of conditions in the where clause
5541 * @param string $id_suffix suffix for the id
5542 * @param string $class css classes for the td element
5544 * @return string the generated HTML
5546 * @access private
5548 * @see _getTableBody(), _getCheckboxAndLinks()
5550 private function _getCheckboxForMultiRowSubmissions(
5551 $del_url, $displayParts, $row_no, $where_clause_html, $condition_array,
5552 $id_suffix, $class
5555 $ret = '';
5557 if (! empty($del_url) && $displayParts['del_lnk'] != self::KILL_PROCESS) {
5559 $ret .= '<td ';
5560 if (! empty($class)) {
5561 $ret .= 'class="' . $class . '"';
5564 $ret .= ' class="center print_ignore">'
5565 . '<input type="checkbox" id="id_rows_to_delete'
5566 . $row_no . $id_suffix
5567 . '" name="rows_to_delete[' . $row_no . ']"'
5568 . ' class="multi_checkbox checkall"'
5569 . ' value="' . $where_clause_html . '" '
5570 . ' />'
5571 . '<input type="hidden" class="condition_array" value="'
5572 . htmlspecialchars(json_encode($condition_array)) . '" />'
5573 . ' </td>';
5576 return $ret;
5578 } // end of the '_getCheckboxForMultiRowSubmissions()' function
5582 * Prepares an Edit link
5584 * @param string $edit_url edit url
5585 * @param string $class css classes for td element
5586 * @param string $edit_str text for the edit link
5587 * @param string $where_clause where clause
5588 * @param string $where_clause_html url encoded where clause
5590 * @return string the generated HTML
5592 * @access private
5594 * @see _getTableBody(), _getCheckboxAndLinks()
5596 private function _getEditLink(
5597 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5600 $ret = '';
5601 if (! empty($edit_url)) {
5603 $ret .= '<td class="' . $class . ' center print_ignore" '
5604 . ' ><span class="nowrap">'
5605 . Util::linkOrButton(
5606 $edit_url, $edit_str, array(), false
5609 * Where clause for selecting this row uniquely is provided as
5610 * a hidden input. Used by jQuery scripts for handling grid editing
5612 if (! empty($where_clause)) {
5613 $ret .= '<input type="hidden" class="where_clause" value ="'
5614 . $where_clause_html . '" />';
5616 $ret .= '</span></td>';
5619 return $ret;
5621 } // end of the '_getEditLink()' function
5625 * Prepares an Copy link
5627 * @param string $copy_url copy url
5628 * @param string $copy_str text for the copy link
5629 * @param string $where_clause where clause
5630 * @param string $where_clause_html url encoded where clause
5631 * @param string $class css classes for the td element
5633 * @return string the generated HTML
5635 * @access private
5637 * @see _getTableBody(), _getCheckboxAndLinks()
5639 private function _getCopyLink(
5640 $copy_url, $copy_str, $where_clause, $where_clause_html, $class
5643 $ret = '';
5644 if (! empty($copy_url)) {
5646 $ret .= '<td class="';
5647 if (! empty($class)) {
5648 $ret .= $class . ' ';
5651 $ret .= 'center print_ignore" ' . ' ><span class="nowrap">'
5652 . Util::linkOrButton(
5653 $copy_url, $copy_str, array(), false
5657 * Where clause for selecting this row uniquely is provided as
5658 * a hidden input. Used by jQuery scripts for handling grid editing
5660 if (! empty($where_clause)) {
5661 $ret .= '<input type="hidden" class="where_clause" value="'
5662 . $where_clause_html . '" />';
5664 $ret .= '</span></td>';
5667 return $ret;
5669 } // end of the '_getCopyLink()' function
5673 * Prepares a Delete link
5675 * @param string $del_url delete url
5676 * @param string $del_str text for the delete link
5677 * @param string $js_conf text for the JS confirmation
5678 * @param string $class css classes for the td element
5680 * @return string the generated HTML
5682 * @access private
5684 * @see _getTableBody(), _getCheckboxAndLinks()
5686 private function _getDeleteLink($del_url, $del_str, $js_conf, $class)
5689 $ret = '';
5690 if (empty($del_url)) {
5691 return $ret;
5694 $ret .= '<td class="';
5695 if (! empty($class)) {
5696 $ret .= $class . ' ';
5698 $ajax = Response::getInstance()->isAjax() ? ' ajax' : '';
5699 $ret .= 'center print_ignore" ' . ' >'
5700 . Util::linkOrButton(
5701 $del_url,
5702 $del_str,
5703 array('class' => 'delete_row requireConfirm' . $ajax),
5704 false
5706 . '<div class="hide">' . $js_conf . '</div>'
5707 . '</td>';
5709 return $ret;
5711 } // end of the '_getDeleteLink()' function
5715 * Prepare checkbox and links at some position (left or right)
5716 * (only called for horizontal mode)
5718 * @param string $position the position of the checkbox and links
5719 * @param string $del_url delete url
5720 * @param array $displayParts array with explicit indexes for all the
5721 * display elements
5722 * @param string $row_no row number
5723 * @param string $where_clause where clause
5724 * @param string $where_clause_html url encoded where clause
5725 * @param array $condition_array array of conditions in the where clause
5726 * @param string $edit_url edit url
5727 * @param string $copy_url copy url
5728 * @param string $class css classes for the td elements
5729 * @param string $edit_str text for the edit link
5730 * @param string $copy_str text for the copy link
5731 * @param string $del_str text for the delete link
5732 * @param string $js_conf text for the JS confirmation
5734 * @return string the generated HTML
5736 * @access private
5738 * @see _getPlacedLinks()
5740 private function _getCheckboxAndLinks(
5741 $position, $del_url, $displayParts, $row_no, $where_clause,
5742 $where_clause_html, $condition_array,
5743 $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf
5746 $ret = '';
5748 if ($position == self::POSITION_LEFT) {
5750 $ret .= $this->_getCheckboxForMultiRowSubmissions(
5751 $del_url, $displayParts, $row_no, $where_clause_html,
5752 $condition_array, '_left', ''
5755 $ret .= $this->_getEditLink(
5756 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5759 $ret .= $this->_getCopyLink(
5760 $copy_url, $copy_str, $where_clause, $where_clause_html, ''
5763 $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
5765 } elseif ($position == self::POSITION_RIGHT) {
5767 $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, '');
5769 $ret .= $this->_getCopyLink(
5770 $copy_url, $copy_str, $where_clause, $where_clause_html, ''
5773 $ret .= $this->_getEditLink(
5774 $edit_url, $class, $edit_str, $where_clause, $where_clause_html
5777 $ret .= $this->_getCheckboxForMultiRowSubmissions(
5778 $del_url, $displayParts, $row_no, $where_clause_html,
5779 $condition_array, '_right', ''
5782 } else { // $position == self::POSITION_NONE
5784 $ret .= $this->_getCheckboxForMultiRowSubmissions(
5785 $del_url, $displayParts, $row_no, $where_clause_html,
5786 $condition_array, '_left', ''
5790 return $ret;
5792 } // end of the '_getCheckboxAndLinks()' function
5795 * Truncates given string based on LimitChars configuration
5796 * and Session pftext variable
5797 * (string is truncated only if necessary)
5799 * @param string $str string to be truncated
5801 * @return mixed
5803 * @access private
5805 * @see _handleNonPrintableContents(), _getDataCellForGeometryColumns(),
5806 * _getDataCellForNonNumericColumns
5808 private function _getPartialText($str)
5810 $original_length = mb_strlen($str);
5811 if ($original_length > $GLOBALS['cfg']['LimitChars']
5812 && $_SESSION['tmpval']['pftext'] === self::DISPLAY_PARTIAL_TEXT
5814 $str = mb_substr(
5815 $str, 0, $GLOBALS['cfg']['LimitChars']
5816 ) . '...';
5817 $truncated = true;
5818 } else {
5819 $truncated = false;
5822 return array($truncated, $str, $original_length);