diff options
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Display/Results.php')
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/Display/Results.php | 5698 |
1 files changed, 5698 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Display/Results.php b/srcs/phpmyadmin/libraries/classes/Display/Results.php new file mode 100644 index 0000000..0fcc5c6 --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/Display/Results.php @@ -0,0 +1,5698 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Hold the PhpMyAdmin\Display\Results class + * + * @package PhpMyAdmin + */ +declare(strict_types=1); + +namespace PhpMyAdmin\Display; + +use PhpMyAdmin\Config\SpecialSchemaLinks; +use PhpMyAdmin\Core; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Index; +use PhpMyAdmin\Message; +use PhpMyAdmin\Plugins\Transformations\Output\Text_Octetstream_Sql; +use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_Json; +use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_Sql; +use PhpMyAdmin\Plugins\Transformations\Text_Plain_Link; +use PhpMyAdmin\Plugins\TransformationsPlugin; +use PhpMyAdmin\Relation; +use PhpMyAdmin\Response; +use PhpMyAdmin\Sanitize; +use PhpMyAdmin\Sql; +use PhpMyAdmin\SqlParser\Statements\SelectStatement; +use PhpMyAdmin\SqlParser\Utils\Query; +use PhpMyAdmin\Table; +use PhpMyAdmin\Template; +use PhpMyAdmin\Transformations; +use PhpMyAdmin\Url; +use PhpMyAdmin\Util; +use stdClass; + +/** + * Handle all the functionalities related to displaying results + * of sql queries, stored procedure, browsing sql processes or + * displaying binary log. + * + * @package PhpMyAdmin + */ +class Results +{ + // Define constants + public const NO_EDIT_OR_DELETE = 'nn'; + public const UPDATE_ROW = 'ur'; + public const DELETE_ROW = 'dr'; + public const KILL_PROCESS = 'kp'; + + public const POSITION_LEFT = 'left'; + public const POSITION_RIGHT = 'right'; + public const POSITION_BOTH = 'both'; + public const POSITION_NONE = 'none'; + + public const DISPLAY_FULL_TEXT = 'F'; + public const DISPLAY_PARTIAL_TEXT = 'P'; + + public const HEADER_FLIP_TYPE_AUTO = 'auto'; + public const HEADER_FLIP_TYPE_CSS = 'css'; + public const HEADER_FLIP_TYPE_FAKE = 'fake'; + + public const DATE_FIELD = 'date'; + public const DATETIME_FIELD = 'datetime'; + public const TIMESTAMP_FIELD = 'timestamp'; + public const TIME_FIELD = 'time'; + public const STRING_FIELD = 'string'; + public const GEOMETRY_FIELD = 'geometry'; + public const BLOB_FIELD = 'BLOB'; + public const BINARY_FIELD = 'BINARY'; + + public const RELATIONAL_KEY = 'K'; + public const RELATIONAL_DISPLAY_COLUMN = 'D'; + + public const GEOMETRY_DISP_GEOM = 'GEOM'; + public const GEOMETRY_DISP_WKT = 'WKT'; + public const GEOMETRY_DISP_WKB = 'WKB'; + + public const SMART_SORT_ORDER = 'SMART'; + public const ASCENDING_SORT_DIR = 'ASC'; + public const DESCENDING_SORT_DIR = 'DESC'; + + public const TABLE_TYPE_INNO_DB = 'InnoDB'; + public const ALL_ROWS = 'all'; + public const QUERY_TYPE_SELECT = 'SELECT'; + + public const ROUTINE_PROCEDURE = 'procedure'; + public const ROUTINE_FUNCTION = 'function'; + + public const ACTION_LINK_CONTENT_ICONS = 'icons'; + public const ACTION_LINK_CONTENT_TEXT = 'text'; + + // Declare global fields + + /** array with properties of the class */ + private $_property_array = [ + + /** integer server id */ + 'server' => null, + + /** string Database name */ + 'db' => null, + + /** string Table name */ + 'table' => null, + + /** string the URL to go back in case of errors */ + 'goto' => null, + + /** string the SQL query */ + 'sql_query' => null, + + /** + * integer the total number of rows returned by the SQL query without any + * appended "LIMIT" clause programmatically + */ + 'unlim_num_rows' => null, + + /** array meta information about fields */ + 'fields_meta' => null, + + /** boolean */ + 'is_count' => null, + + /** integer */ + 'is_export' => null, + + /** boolean */ + 'is_func' => null, + + /** integer */ + 'is_analyse' => null, + + /** integer the total number of rows returned by the SQL query */ + 'num_rows' => null, + + /** integer the total number of fields returned by the SQL query */ + 'fields_cnt' => null, + + /** double time taken for execute the SQL query */ + 'querytime' => null, + + /** string path for theme images directory */ + 'pma_theme_image' => null, + + /** string */ + 'text_dir' => null, + + /** boolean */ + 'is_maint' => null, + + /** boolean */ + 'is_explain' => null, + + /** boolean */ + 'is_show' => null, + + /** boolean */ + 'is_browse_distinct' => null, + + /** array table definitions */ + 'showtable' => null, + + /** string */ + 'printview' => null, + + /** string URL query */ + 'url_query' => null, + + /** array column names to highlight */ + 'highlight_columns' => null, + + /** array holding various display information */ + 'display_params' => null, + + /** array mime types information of fields */ + 'mime_map' => null, + + /** boolean */ + 'editable' => null, + + /** random unique ID to distinguish result set */ + 'unique_id' => null, + + /** where clauses for each row, each table in the row */ + 'whereClauseMap' => [], + ]; + + /** + * This variable contains the column transformation information + * for some of the system databases. + * One element of this array represent all relevant columns in all tables in + * one specific database + */ + public $transformation_info; + + /** + * @var Relation + */ + private $relation; + + /** + * @var Transformations + */ + private $transformations; + + /** + * @var Template + */ + public $template; + + /** + * Constructor for PhpMyAdmin\Display\Results class + * + * @param string $db the database name + * @param string $table the table name + * @param int $server the server id + * @param string $goto the URL to go back in case of errors + * @param string $sql_query the SQL query + * + * @access public + */ + public function __construct($db, $table, $server, $goto, $sql_query) + { + $this->relation = new Relation($GLOBALS['dbi']); + $this->transformations = new Transformations(); + $this->template = new Template(); + + $this->_setDefaultTransformations(); + + $this->__set('db', $db); + $this->__set('table', $table); + $this->__set('server', $server); + $this->__set('goto', $goto); + $this->__set('sql_query', $sql_query); + $this->__set('unique_id', mt_rand()); + } + + /** + * Get any property of this class + * + * @param string $property name of the property + * + * @return mixed|void if property exist, value of the relevant property + */ + public function __get($property) + { + return $this->_property_array[$property] ?? null; + } + + /** + * Set values for any property of this class + * + * @param string $property name of the property + * @param mixed $value value to set + * + * @return void + */ + public function __set($property, $value) + { + if (array_key_exists($property, $this->_property_array)) { + $this->_property_array[$property] = $value; + } + } + + /** + * Sets default transformations for some columns + * + * @return void + */ + private function _setDefaultTransformations() + { + $json_highlighting_data = [ + 'libraries/classes/Plugins/Transformations/Output/Text_Plain_Json.php', + Text_Plain_Json::class, + 'Text_Plain', + ]; + $sql_highlighting_data = [ + 'libraries/classes/Plugins/Transformations/Output/Text_Plain_Sql.php', + Text_Plain_Sql::class, + 'Text_Plain', + ]; + $blob_sql_highlighting_data = [ + 'libraries/classes/Plugins/Transformations/Output/Text_Octetstream_Sql.php', + Text_Octetstream_Sql::class, + 'Text_Octetstream', + ]; + $link_data = [ + 'libraries/classes/Plugins/Transformations/Text_Plain_Link.php', + Text_Plain_Link::class, + 'Text_Plain', + ]; + $this->transformation_info = [ + 'information_schema' => [ + 'events' => [ + 'event_definition' => $sql_highlighting_data, + ], + 'processlist' => [ + 'info' => $sql_highlighting_data, + ], + 'routines' => [ + 'routine_definition' => $sql_highlighting_data, + ], + 'triggers' => [ + 'action_statement' => $sql_highlighting_data, + ], + 'views' => [ + 'view_definition' => $sql_highlighting_data, + ], + ], + 'mysql' => [ + 'event' => [ + 'body' => $blob_sql_highlighting_data, + 'body_utf8' => $blob_sql_highlighting_data, + ], + 'general_log' => [ + 'argument' => $sql_highlighting_data, + ], + 'help_category' => [ + 'url' => $link_data, + ], + 'help_topic' => [ + 'example' => $sql_highlighting_data, + 'url' => $link_data, + ], + 'proc' => [ + 'param_list' => $blob_sql_highlighting_data, + 'returns' => $blob_sql_highlighting_data, + 'body' => $blob_sql_highlighting_data, + 'body_utf8' => $blob_sql_highlighting_data, + ], + 'slow_log' => [ + 'sql_text' => $sql_highlighting_data, + ], + ], + ]; + + $cfgRelation = $this->relation->getRelationsParam(); + if ($cfgRelation['db']) { + $this->transformation_info[$cfgRelation['db']] = []; + $relDb = &$this->transformation_info[$cfgRelation['db']]; + if (! empty($cfgRelation['history'])) { + $relDb[$cfgRelation['history']] = [ + 'sqlquery' => $sql_highlighting_data, + ]; + } + if (! empty($cfgRelation['bookmark'])) { + $relDb[$cfgRelation['bookmark']] = [ + 'query' => $sql_highlighting_data, + ]; + } + if (! empty($cfgRelation['tracking'])) { + $relDb[$cfgRelation['tracking']] = [ + 'schema_sql' => $sql_highlighting_data, + 'data_sql' => $sql_highlighting_data, + ]; + } + if (! empty($cfgRelation['favorite'])) { + $relDb[$cfgRelation['favorite']] = [ + 'tables' => $json_highlighting_data, + ]; + } + if (! empty($cfgRelation['recent'])) { + $relDb[$cfgRelation['recent']] = [ + 'tables' => $json_highlighting_data, + ]; + } + if (! empty($cfgRelation['savedsearches'])) { + $relDb[$cfgRelation['savedsearches']] = [ + 'search_data' => $json_highlighting_data, + ]; + } + if (! empty($cfgRelation['designer_settings'])) { + $relDb[$cfgRelation['designer_settings']] = [ + 'settings_data' => $json_highlighting_data, + ]; + } + if (! empty($cfgRelation['table_uiprefs'])) { + $relDb[$cfgRelation['table_uiprefs']] = [ + 'prefs' => $json_highlighting_data, + ]; + } + if (! empty($cfgRelation['userconfig'])) { + $relDb[$cfgRelation['userconfig']] = [ + 'config_data' => $json_highlighting_data, + ]; + } + if (! empty($cfgRelation['export_templates'])) { + $relDb[$cfgRelation['export_templates']] = [ + 'template_data' => $json_highlighting_data, + ]; + } + } + } + + /** + * Set properties which were not initialized at the constructor + * + * @param integer $unlim_num_rows the total number of rows returned by + * the SQL query without any appended + * "LIMIT" clause programmatically + * @param stdClass $fields_meta meta information about fields + * @param boolean $is_count statement is SELECT COUNT + * @param integer $is_export statement contains INTO OUTFILE + * @param boolean $is_func statement contains a function like SUM() + * @param integer $is_analyse statement contains PROCEDURE ANALYSE + * @param integer $num_rows total no. of rows returned by SQL query + * @param integer $fields_cnt total no.of fields returned by SQL query + * @param double $querytime time taken for execute the SQL query + * @param string $pmaThemeImage path for theme images directory + * @param string $text_dir text direction + * @param boolean $is_maint statement contains a maintenance command + * @param boolean $is_explain statement contains EXPLAIN + * @param boolean $is_show statement contains SHOW + * @param array $showtable table definitions + * @param string $printview print view was requested + * @param string $url_query URL query + * @param boolean $editable whether the results set is editable + * @param boolean $is_browse_dist whether browsing distinct values + * + * @return void + * + * @see sql.php + */ + public function setProperties( + $unlim_num_rows, + $fields_meta, + $is_count, + $is_export, + $is_func, + $is_analyse, + $num_rows, + $fields_cnt, + $querytime, + $pmaThemeImage, + $text_dir, + $is_maint, + $is_explain, + $is_show, + $showtable, + $printview, + $url_query, + $editable, + $is_browse_dist + ) { + + $this->__set('unlim_num_rows', $unlim_num_rows); + $this->__set('fields_meta', $fields_meta); + $this->__set('is_count', $is_count); + $this->__set('is_export', $is_export); + $this->__set('is_func', $is_func); + $this->__set('is_analyse', $is_analyse); + $this->__set('num_rows', $num_rows); + $this->__set('fields_cnt', $fields_cnt); + $this->__set('querytime', $querytime); + $this->__set('pma_theme_image', $pmaThemeImage); + $this->__set('text_dir', $text_dir); + $this->__set('is_maint', $is_maint); + $this->__set('is_explain', $is_explain); + $this->__set('is_show', $is_show); + $this->__set('showtable', $showtable); + $this->__set('printview', $printview); + $this->__set('url_query', $url_query); + $this->__set('editable', $editable); + $this->__set('is_browse_distinct', $is_browse_dist); + } + + /** + * Defines the parts to display for a print view + * + * @param array $displayParts the parts to display + * + * @return array the modified display parts + * + * @access private + * + */ + private function _setDisplayPartsForPrintView(array $displayParts) + { + // set all elements to false! + $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link + $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link + $displayParts['sort_lnk'] = (string) '0'; + $displayParts['nav_bar'] = (string) '0'; + $displayParts['bkm_form'] = (string) '0'; + $displayParts['text_btn'] = (string) '0'; + $displayParts['pview_lnk'] = (string) '0'; + + return $displayParts; + } + + /** + * Defines the parts to display for a SHOW statement + * + * @param array $displayParts the parts to display + * + * @return array the modified display parts + * + * @access private + * + */ + private function _setDisplayPartsForShow(array $displayParts) + { + preg_match( + '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?' + . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS' + . ')@i', + $this->__get('sql_query'), + $which + ); + + $bIsProcessList = isset($which[1]); + if ($bIsProcessList) { + $str = ' ' . strtoupper($which[1]); + $bIsProcessList = $bIsProcessList + && strpos($str, 'PROCESSLIST') > 0; + } + + if ($bIsProcessList) { + // no edit link + $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; + // "kill process" type edit link + $displayParts['del_lnk'] = self::KILL_PROCESS; + } else { + // Default case -> no links + // no edit link + $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; + // no delete link + $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; + } + // Other settings + $displayParts['sort_lnk'] = (string) '0'; + $displayParts['nav_bar'] = (string) '0'; + $displayParts['bkm_form'] = (string) '1'; + $displayParts['text_btn'] = (string) '1'; + $displayParts['pview_lnk'] = (string) '1'; + + return $displayParts; + } + + /** + * Defines the parts to display for statements not related to data + * + * @param array $displayParts the parts to display + * + * @return array the modified display parts + * + * @access private + * + */ + private function _setDisplayPartsForNonData(array $displayParts) + { + // Statement is a "SELECT COUNT", a + // "CHECK/ANALYZE/REPAIR/OPTIMIZE/CHECKSUM", an "EXPLAIN" one or + // contains a "PROC ANALYSE" part + $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link + $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link + $displayParts['sort_lnk'] = (string) '0'; + $displayParts['nav_bar'] = (string) '0'; + $displayParts['bkm_form'] = (string) '1'; + + if ($this->__get('is_maint')) { + $displayParts['text_btn'] = (string) '1'; + } else { + $displayParts['text_btn'] = (string) '0'; + } + $displayParts['pview_lnk'] = (string) '1'; + + return $displayParts; + } + + /** + * Defines the parts to display for other statements (probably SELECT) + * + * @param array $displayParts the parts to display + * + * @return array the modified display parts + * + * @access private + * + */ + private function _setDisplayPartsForSelect(array $displayParts) + { + // Other statements (ie "SELECT" ones) -> updates + // $displayParts['edit_lnk'], $displayParts['del_lnk'] and + // $displayParts['text_btn'] (keeps other default values) + + $fields_meta = $this->__get('fields_meta'); + $prev_table = ''; + $displayParts['text_btn'] = (string) '1'; + $number_of_columns = $this->__get('fields_cnt'); + + for ($i = 0; $i < $number_of_columns; $i++) { + $is_link = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) + || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE) + || ($displayParts['sort_lnk'] != '0'); + + // Displays edit/delete/sort/insert links? + if ($is_link + && $prev_table != '' + && $fields_meta[$i]->table != '' + && $fields_meta[$i]->table != $prev_table + ) { + // don't display links + $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; + $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; + /** + * @todo May be problematic with same field names + * in two joined table. + */ + // $displayParts['sort_lnk'] = (string) '0'; + if ($displayParts['text_btn'] == '1') { + break; + } + } // end if + + // Always display print view link + $displayParts['pview_lnk'] = (string) '1'; + if ($fields_meta[$i]->table != '') { + $prev_table = $fields_meta[$i]->table; + } + } // end for + + if ($prev_table == '') { // no table for any of the columns + // don't display links + $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; + $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; + } + + return $displayParts; + } + + /** + * Defines the parts to display for the results of a SQL query + * and the total number of rows + * + * @param array $displayParts the parts to display (see a few + * lines above for explanations) + * + * @return array the first element is an array with explicit indexes + * for all the display elements + * the second element is the total number of rows returned + * by the SQL query without any programmatically appended + * LIMIT clause (just a copy of $unlim_num_rows if it exists, + * else computed inside this function) + * + * + * @access private + * + * @see getTable() + */ + private function _setDisplayPartsAndTotal(array $displayParts) + { + $the_total = 0; + + // 1. Following variables are needed for use in isset/empty or + // use with array indexes or safe use in foreach + $db = $this->__get('db'); + $table = $this->__get('table'); + $unlim_num_rows = $this->__get('unlim_num_rows'); + $num_rows = $this->__get('num_rows'); + $printview = $this->__get('printview'); + + // 2. Updates the display parts + if ($printview == '1') { + $displayParts = $this->_setDisplayPartsForPrintView($displayParts); + } elseif ($this->__get('is_count') || $this->__get('is_analyse') + || $this->__get('is_maint') || $this->__get('is_explain') + ) { + $displayParts = $this->_setDisplayPartsForNonData($displayParts); + } elseif ($this->__get('is_show')) { + $displayParts = $this->_setDisplayPartsForShow($displayParts); + } else { + $displayParts = $this->_setDisplayPartsForSelect($displayParts); + } // end if..elseif...else + + // 3. Gets the total number of rows if it is unknown + if (isset($unlim_num_rows) && $unlim_num_rows != '') { + $the_total = $unlim_num_rows; + } elseif (($displayParts['nav_bar'] == '1') + || ($displayParts['sort_lnk'] == '1') + && (strlen($db) > 0 && strlen($table) > 0) + ) { + $the_total = $GLOBALS['dbi']->getTable($db, $table)->countRecords(); + } + + // if for COUNT query, number of rows returned more than 1 + // (may be being used GROUP BY) + if ($this->__get('is_count') && isset($num_rows) && $num_rows > 1) { + $displayParts['nav_bar'] = (string) '1'; + $displayParts['sort_lnk'] = (string) '1'; + } + // 4. If navigation bar or sorting fields names URLs should be + // displayed but there is only one row, change these settings to + // false + if ($displayParts['nav_bar'] == '1' || $displayParts['sort_lnk'] == '1') { + // - Do not display sort links if less than 2 rows. + // - For a VIEW we (probably) did not count the number of rows + // so don't test this number here, it would remove the possibility + // of sorting VIEW results. + $_table = new Table($table, $db); + if (isset($unlim_num_rows) + && ($unlim_num_rows < 2) + && ! $_table->isView() + ) { + $displayParts['sort_lnk'] = (string) '0'; + } + } // end if (3) + + return [ + $displayParts, + $the_total, + ]; + } + + /** + * Return true if we are executing a query in the form of + * "SELECT * FROM <a table> ..." + * + * @param array $analyzed_sql_results analyzed sql results + * + * @return boolean + * + * @access private + * + * @see _getTableHeaders(), _getColumnParams() + */ + private function _isSelect(array $analyzed_sql_results) + { + return ! ($this->__get('is_count') + || $this->__get('is_export') + || $this->__get('is_func') + || $this->__get('is_analyse')) + && ! empty($analyzed_sql_results['select_from']) + && ! empty($analyzed_sql_results['statement']->from) + && (count($analyzed_sql_results['statement']->from) === 1) + && ! empty($analyzed_sql_results['statement']->from[0]->table); + } + + /** + * Get a navigation button + * + * @param string $caption iconic caption for button + * @param string $title text for button + * @param integer $pos position for next query + * @param string $html_sql_query query ready for display + * @param boolean $back whether 'begin' or 'previous' + * @param string $onsubmit optional onsubmit clause + * @param string $input_for_real_end optional hidden field for special treatment + * @param string $onclick optional onclick clause + * + * @return string html content + * + * @access private + * + * @see _getMoveBackwardButtonsForTableNavigation(), + * _getMoveForwardButtonsForTableNavigation() + */ + private function _getTableNavigationButton( + $caption, + $title, + $pos, + $html_sql_query, + $back, + $onsubmit = '', + $input_for_real_end = '', + $onclick = '' + ) { + $caption_output = ''; + if ($back) { + if (Util::showIcons('TableNavigationLinksMode')) { + $caption_output .= $caption; + } + if (Util::showText('TableNavigationLinksMode')) { + $caption_output .= ' ' . $title; + } + } else { + if (Util::showText('TableNavigationLinksMode')) { + $caption_output .= $title; + } + if (Util::showIcons('TableNavigationLinksMode')) { + $caption_output .= ' ' . $caption; + } + } + + return $this->template->render('display/results/table_navigation_button', [ + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $html_sql_query, + 'pos' => $pos, + 'is_browse_distinct' => $this->__get('is_browse_distinct'), + 'goto' => $this->__get('goto'), + 'input_for_real_end' => $input_for_real_end, + 'caption_output' => $caption_output, + 'title' => $title, + 'onsubmit' => $onsubmit, + 'onclick' => $onclick, + ]); + } + + /** + * Possibly return a page selector for table navigation + * + * @return array ($output, $nbTotalPage) + * + * @access private + */ + private function _getHtmlPageSelector(): array + { + $pageNow = @floor( + $_SESSION['tmpval']['pos'] + / $_SESSION['tmpval']['max_rows'] + ) + 1; + + $nbTotalPage = @ceil( + $this->__get('unlim_num_rows') + / $_SESSION['tmpval']['max_rows'] + ); + + $output = ''; + if ($nbTotalPage > 1) { + $_url_params = [ + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $this->__get('sql_query'), + 'goto' => $this->__get('goto'), + 'is_browse_distinct' => $this->__get('is_browse_distinct'), + ]; + + $output = $this->template->render('display/results/page_selector', [ + 'url_params' => $_url_params, + 'page_selector' => Util::pageselector( + 'pos', + $_SESSION['tmpval']['max_rows'], + $pageNow, + $nbTotalPage, + 200, + 5, + 5, + 20, + 10 + ), + ]); + } + return [ + $output, + $nbTotalPage, + ]; + } + + /** + * Get a navigation bar to browse among the results of a SQL query + * + * @param integer $posNext the offset for the "next" page + * @param integer $posPrevious the offset for the "previous" page + * @param boolean $isInnodb whether its InnoDB or not + * @param string $sortByKeyHtml the sort by key dialog + * + * @return string html content + * + * @access private + * + * @see getTable() + */ + private function _getTableNavigation( + $posNext, + $posPrevious, + $isInnodb, + $sortByKeyHtml + ): string { + $isShowingAll = $_SESSION['tmpval']['max_rows'] === self::ALL_ROWS; + + // Move to the beginning or to the previous page + $moveBackwardButtons = ''; + if ($_SESSION['tmpval']['pos'] && ! $isShowingAll) { + $moveBackwardButtons = $this->_getMoveBackwardButtonsForTableNavigation( + htmlspecialchars($this->__get('sql_query')), + $posPrevious + ); + } + + $pageSelector = ''; + $numberTotalPage = 1; + if (! $isShowingAll) { + list( + $pageSelector, + $numberTotalPage + ) = $this->_getHtmlPageSelector(); + } + + // Move to the next page or to the last one + $moveForwardButtons = ''; + if ($this->__get('unlim_num_rows') === false // view with unknown number of rows + || (! $isShowingAll + && $_SESSION['tmpval']['pos'] + $_SESSION['tmpval']['max_rows'] < $this->__get('unlim_num_rows') + && $this->__get('num_rows') >= $_SESSION['tmpval']['max_rows']) + ) { + $moveForwardButtons = $this->_getMoveForwardButtonsForTableNavigation( + htmlspecialchars($this->__get('sql_query')), + $posNext, + $isInnodb + ); + } + + $hiddenFields = [ + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'server' => $this->__get('server'), + 'sql_query' => $this->__get('sql_query'), + 'is_browse_distinct' => $this->__get('is_browse_distinct'), + 'goto' => $this->__get('goto'), + ]; + + return $this->template->render('display/results/table_navigation', [ + 'move_backward_buttons' => $moveBackwardButtons, + 'page_selector' => $pageSelector, + 'move_forward_buttons' => $moveForwardButtons, + 'number_total_page' => $numberTotalPage, + 'has_show_all' => $GLOBALS['cfg']['ShowAll'] || ($this->__get('unlim_num_rows') <= 500), + 'hidden_fields' => $hiddenFields, + 'session_max_rows' => $isShowingAll ? $GLOBALS['cfg']['MaxRows'] : 'all', + 'unique_id' => $this->__get('unique_id'), + 'is_showing_all' => $isShowingAll, + 'unlim_num_rows' => $this->__get('unlim_num_rows'), + 'max_rows' => $_SESSION['tmpval']['max_rows'], + 'pos' => $_SESSION['tmpval']['pos'], + 'sort_by_key' => $sortByKeyHtml, + ]); + } + + /** + * Prepare move backward buttons - previous and first + * + * @param string $html_sql_query the sql encoded by html special characters + * @param integer $pos_prev the offset for the "previous" page + * + * @return string html content + * + * @access private + * + * @see _getTableNavigation() + */ + private function _getMoveBackwardButtonsForTableNavigation( + $html_sql_query, + $pos_prev + ) { + return $this->_getTableNavigationButton( + '<<', + _pgettext('First page', 'Begin'), + 0, + $html_sql_query, + true + ) + . $this->_getTableNavigationButton( + '<', + _pgettext('Previous page', 'Previous'), + $pos_prev, + $html_sql_query, + true + ); + } + + /** + * Prepare move forward buttons - next and last + * + * @param string $html_sql_query the sql encoded by htmlspecialchars() + * @param integer $pos_next the offset for the "next" page + * @param boolean $is_innodb whether it's InnoDB or not + * + * @return string html content + * + * @access private + * + * @see _getTableNavigation() + */ + private function _getMoveForwardButtonsForTableNavigation( + $html_sql_query, + $pos_next, + $is_innodb + ) { + // display the Next button + $buttons_html = $this->_getTableNavigationButton( + '>', + _pgettext('Next page', 'Next'), + $pos_next, + $html_sql_query, + false + ); + + // prepare some options for the End button + if ($is_innodb + && $this->__get('unlim_num_rows') > $GLOBALS['cfg']['MaxExactCount'] + ) { + $input_for_real_end = '<input id="real_end_input" type="hidden" ' + . 'name="find_real_end" value="1">'; + // no backquote around this message + $onclick = ''; + } else { + $input_for_real_end = $onclick = ''; + } + + $maxRows = $_SESSION['tmpval']['max_rows']; + $onsubmit = 'onsubmit="return ' + . ($_SESSION['tmpval']['pos'] + + $maxRows + < $this->__get('unlim_num_rows') + && $this->__get('num_rows') >= $maxRows + ? 'true' + : 'false') . '"'; + + // display the End button + $buttons_html .= $this->_getTableNavigationButton( + '>>', + _pgettext('Last page', 'End'), + @((ceil( |
