diff options
Diffstat (limited to 'hugo/libraries/DisplayResults.class.php')
| -rw-r--r-- | hugo/libraries/DisplayResults.class.php | 6013 |
1 files changed, 6013 insertions, 0 deletions
diff --git a/hugo/libraries/DisplayResults.class.php b/hugo/libraries/DisplayResults.class.php new file mode 100644 index 0000000..b4ec6a7 --- /dev/null +++ b/hugo/libraries/DisplayResults.class.php @@ -0,0 +1,6013 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Hold the PMA_DisplayResults class + * + * @package PhpMyAdmin + */ +if (! defined('PHPMYADMIN')) { + exit; +} + +/** + * Handle all the functionalities related to displaying results + * of sql queries, stored procedure, browsing sql processes or + * displaying binary log. + * + * @package PhpMyAdmin + */ +class PMA_DisplayResults +{ + + // Define constants + const NO_EDIT_OR_DELETE = 'nn'; + const UPDATE_ROW = 'ur'; + const DELETE_ROW = 'dr'; + const KILL_PROCESS = 'kp'; + + const POSITION_LEFT = 'left'; + const POSITION_RIGHT = 'right'; + const POSITION_BOTH = 'both'; + const POSITION_NONE = 'none'; + + const PLACE_TOP_DIRECTION_DROPDOWN = 'top_direction_dropdown'; + const PLACE_BOTTOM_DIRECTION_DROPDOWN = 'bottom_direction_dropdown'; + + const DISP_DIR_HORIZONTAL = 'horizontal'; + const DISP_DIR_HORIZONTAL_FLIPPED = 'horizontalflipped'; + const DISP_DIR_VERTICAL = 'vertical'; + + const DISPLAY_FULL_TEXT = 'F'; + const DISPLAY_PARTIAL_TEXT = 'P'; + + const HEADER_FLIP_TYPE_AUTO = 'auto'; + const HEADER_FLIP_TYPE_CSS = 'css'; + const HEADER_FLIP_TYPE_FAKE = 'fake'; + + const DATE_FIELD = 'date'; + const DATETIME_FIELD = 'datetime'; + const TIMESTAMP_FIELD = 'timestamp'; + const STRING_FIELD = 'string'; + const GEOMETRY_FIELD = 'geometry'; + const BLOB_FIELD = 'BLOB'; + const BINARY_FIELD = 'BINARY'; + + const RELATIONAL_KEY = 'K'; + const RELATIONAL_DISPLAY_COLUMN = 'D'; + + const GEOMETRY_DISP_GEOM = 'GEOM'; + const GEOMETRY_DISP_WKT = 'WKT'; + const GEOMETRY_DISP_WKB = 'WKB'; + + const SMART_SORT_ORDER = 'SMART'; + const ASCENDING_SORT_DIR = 'ASC'; + const DESCENDING_SORT_DIR = 'DESC'; + + const TABLE_TYPE_INNO_DB = 'InnoDB'; + const ALL_ROWS = 'all'; + const QUERY_TYPE_SELECT = 'SELECT'; + + const ROUTINE_PROCEDURE = 'procedure'; + const ROUTINE_FUNCTION = 'function'; + + + // Declare global fields + + /** array with properties of the class */ + private $_property_array = array( + + /** 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, + + /** array table definitions */ + 'showtable' => null, + + /** string */ + 'printview' => null, + + /** string URL query */ + 'url_query' => null, + + /** array column names to highlight */ + 'highlight_columns' => null, + + /** array informations used with vertical display mode */ + 'vertical_display' => null, + + /** array mime types information of fields */ + 'mime_map' => null, + + /** boolean */ + 'editable' => null + ); + + /** + * This variable contains the column transformation information + * for some of the system databases. + * One element of this array represent all relavant columns in all tables in + * one specific database + */ + public $transformation_info; + + + /** + * Get any property of this class + * + * @param string $property name of the property + * + * @return if property exist, value of the relavant property + */ + public function __get($property) + { + if (array_key_exists($property, $this->_property_array)) { + return $this->_property_array[$property]; + } + } + + + /** + * Set values for any property of this class + * + * @param string $property name of the property + * @param any $value value to set + * + * @return void + */ + public function __set($property, $value) + { + if (array_key_exists($property, $this->_property_array)) { + $this->_property_array[$property] = $value; + } + } + + + /** + * Constructor for PMA_DisplayResults class + * + * @param string $db the database name + * @param string $table the table name + * @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, $goto, $sql_query) + { + $this->_setDefaultTransformations(); + + $this->__set('db', $db); + $this->__set('table', $table); + $this->__set('goto', $goto); + $this->__set('sql_query', $sql_query); + } + + /** + * Sets default transformations for some columns + * + * @return void + */ + private function _setDefaultTransformations() + { + $sql_highlighting_data = array( + 'libraries/plugins/transformations/Text_Plain_Formatted.class.php', + 'Text_Plain_Formatted', + 'Text_Plain' + ); + $this->transformation_info = array( + 'information_schema' => array( + 'events' => array( + 'event_definition' => $sql_highlighting_data + ), + 'processlist' => array( + 'info' => $sql_highlighting_data + ), + 'routines' => array( + 'routine_definition' => $sql_highlighting_data + ), + 'triggers' => array( + 'action_statement' => $sql_highlighting_data + ), + 'views' => array( + 'view_definition' => $sql_highlighting_data + ) + ) + ); + + $cfgRelation = PMA_getRelationsParam(); + if ($cfgRelation['db']) { + $this->transformation_info[$cfgRelation['db']] = array(); + $relDb = &$this->transformation_info[$cfgRelation['db']]; + if ($cfgRelation['history']) { + $relDb[$cfgRelation['history']] = array( + 'sqlquery' => $sql_highlighting_data + ); + } + if ($cfgRelation['bookmark']) { + $relDb[$cfgRelation['bookmark']] = array( + 'query' => $sql_highlighting_data + ); + } + if ($cfgRelation['tracking']) { + $relDb[$cfgRelation['tracking']] = array( + 'schema_sql' => $sql_highlighting_data, + 'data_sql' => $sql_highlighting_data + ); + } + } + } + + /** + * Set properties which were not initialized at the constructor + * + * @param type $unlim_num_rows integer the total number of rows returned by + * the SQL query without any appended + * "LIMIT" clause programmatically + * @param type $fields_meta array meta information about fields + * @param type $is_count boolean + * @param type $is_export integer + * @param type $is_func boolean + * @param type $is_analyse integer + * @param type $num_rows integer total no. of rows returned by SQL query + * @param type $fields_cnt integer total no.of fields returned by SQL query + * @param type $querytime double time taken for execute the SQL query + * @param type $pmaThemeImage string path for theme images directory + * @param type $text_dir string + * @param type $is_maint boolean + * @param type $is_explain boolean + * @param type $is_show boolean + * @param type $showtable array table definitions + * @param type $printview string + * @param type $url_query string URL query + * @param type $editable boolean whether the resutls set is editable + * + * @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 + ) { + + $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); + + } // end of the 'setProperties()' function + + + /** + * Defines the display mode to use for the results of a SQL query + * + * It uses a synthetic string that contains all the required informations. + * In this string: + * - the first two characters stand for the action to do while + * clicking on the "edit" link (e.g. 'ur' for update a row, 'nn' for no + * edit link...); + * - the next two characters stand for the action to do while + * clicking on the "delete" link (e.g. 'kp' for kill a process, 'nn' for + * no delete link...); + * - the next characters are boolean values (1/0) and respectively stand + * for sorting links, navigation bar, "insert a new row" link, the + * bookmark feature, the expand/collapse text/blob fields button and + * the "display printable view" option. + * Of course '0'/'1' means the feature won't/will be enabled. + * + * @param string &$the_disp_mode the synthetic value for display_mode (see a few + * lines above for explanations) + * @param integer &$the_total 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, + * elsecomputed inside this function) + * + * @return array an array with explicit indexes for all the display + * elements + * + * @access private + * + * @see getTable() + */ + private function _setDisplayMode(&$the_disp_mode, &$the_total) + { + + // 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'); + $fields_meta = $this->__get('fields_meta'); + $printview = $this->__get('printview'); + + // 1. Initializes the $do_display array + $do_display = array(); + $do_display['edit_lnk'] = $the_disp_mode[0] . $the_disp_mode[1]; + $do_display['del_lnk'] = $the_disp_mode[2] . $the_disp_mode[3]; + $do_display['sort_lnk'] = (string) $the_disp_mode[4]; + $do_display['nav_bar'] = (string) $the_disp_mode[5]; + $do_display['ins_row'] = (string) $the_disp_mode[6]; + $do_display['bkm_form'] = (string) $the_disp_mode[7]; + $do_display['text_btn'] = (string) $the_disp_mode[8]; + $do_display['pview_lnk'] = (string) $the_disp_mode[9]; + + // 2. Display mode is not "false for all elements" -> updates the + // display mode + if ($the_disp_mode != 'nnnn000000') { + + if (isset($printview) && ($printview == '1')) { + // 2.0 Print view -> set all elements to false! + $do_display['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link + $do_display['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link + $do_display['sort_lnk'] = (string) '0'; + $do_display['nav_bar'] = (string) '0'; + $do_display['ins_row'] = (string) '0'; + $do_display['bkm_form'] = (string) '0'; + $do_display['text_btn'] = (string) '0'; + $do_display['pview_lnk'] = (string) '0'; + + } elseif ($this->__get('is_count') || $this->__get('is_analyse') + || $this->__get('is_maint') || $this->__get('is_explain') + ) { + // 2.1 Statement is a "SELECT COUNT", a + // "CHECK/ANALYZE/REPAIR/OPTIMIZE", an "EXPLAIN" one or + // contains a "PROC ANALYSE" part + $do_display['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link + $do_display['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link + $do_display['sort_lnk'] = (string) '0'; + $do_display['nav_bar'] = (string) '0'; + $do_display['ins_row'] = (string) '0'; + $do_display['bkm_form'] = (string) '1'; + + if ($this->__get('is_maint')) { + $do_display['text_btn'] = (string) '1'; + } else { + $do_display['text_btn'] = (string) '0'; + } + $do_display['pview_lnk'] = (string) '1'; + + } elseif ($this->__get('is_show')) { + // 2.2 Statement is a "SHOW..." + /** + * 2.2.1 + * @todo defines edit/delete links depending on show statement + */ + $tmp = preg_match( + '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?' + . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS' + . ')@i', + $this->__get('sql_query'), $which + ); + if (isset($which[1]) + && (strpos(' ' . strtoupper($which[1]), 'PROCESSLIST') > 0) + ) { + // no edit link + $do_display['edit_lnk'] = self::NO_EDIT_OR_DELETE; + // "kill process" type edit link + $do_display['del_lnk'] = self::KILL_PROCESS; + } else { + // Default case -> no links + // no edit link + $do_display['edit_lnk'] = self::NO_EDIT_OR_DELETE; + // no delete link + $do_display['del_lnk'] = self::NO_EDIT_OR_DELETE; + } + // 2.2.2 Other settings + $do_display['sort_lnk'] = (string) '0'; + $do_display['nav_bar'] = (string) '0'; + $do_display['ins_row'] = (string) '0'; + $do_display['bkm_form'] = (string) '1'; + $do_display['text_btn'] = (string) '1'; + $do_display['pview_lnk'] = (string) '1'; + + } else { + // 2.3 Other statements (ie "SELECT" ones) -> updates + // $do_display['edit_lnk'], $do_display['del_lnk'] and + // $do_display['text_btn'] (keeps other default values) + $prev_table = $fields_meta[0]->table; + $do_display['text_btn'] = (string) '1'; + + for ($i = 0; $i < $this->__get('fields_cnt'); $i++) { + + $is_link = ($do_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + || ($do_display['del_lnk'] != self::NO_EDIT_OR_DELETE) + || ($do_display['sort_lnk'] != '0') + || ($do_display['ins_row'] != '0'); + + // 2.3.2 Displays edit/delete/sort/insert links? + if ($is_link + && (($fields_meta[$i]->table == '') + || ($fields_meta[$i]->table != $prev_table)) + ) { + // don't display links + $do_display['edit_lnk'] = self::NO_EDIT_OR_DELETE; + $do_display['del_lnk'] = self::NO_EDIT_OR_DELETE; + /** + * @todo May be problematic with same field names + * in two joined table. + */ + // $do_display['sort_lnk'] = (string) '0'; + $do_display['ins_row'] = (string) '0'; + if ($do_display['text_btn'] == '1') { + break; + } + } // end if (2.3.2) + + // 2.3.3 Always display print view link + $do_display['pview_lnk'] = (string) '1'; + $prev_table = $fields_meta[$i]->table; + + } // end for + } // end if..elseif...else (2.1 -> 2.3) + } // end if (2) + + // 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 ((($do_display['nav_bar'] == '1') + || ($do_display['sort_lnk'] == '1')) + && (strlen($db) && !empty($table)) + ) { + $the_total = PMA_Table::countRecords($db, $table); + } + + // 4. If navigation bar or sorting fields names URLs should be + // displayed but there is only one row, change these settings to + // false + if ($do_display['nav_bar'] == '1' || $do_display['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. + if (isset($unlim_num_rows) + && ($unlim_num_rows < 2) + && ! PMA_Table::isView($db, $table) + ) { + // force display of navbar for vertical/horizontal display-choice. + // $do_display['nav_bar'] = (string) '0'; + $do_display['sort_lnk'] = (string) '0'; + } + } // end if (3) + + // 5. Updates the synthetic var + $the_disp_mode = join('', $do_display); + + return $do_display; + + } // end of the 'setDisplayMode()' function + + + /** + * Return true if we are executing a query in the form of + * "SELECT * FROM <a table> ..." + * + * @param array $analyzed_sql the analyzed query + * + * @return boolean + * + * @access private + * + * @see _getTableHeaders(), _getColumnParams() + */ + private function _isSelect($analyzed_sql) + { + if (!isset($analyzed_sql[0]['select_expr'])) { + $analyzed_sql[0]['select_expr'] = 0; + } + + return ! ($this->__get('is_count') || $this->__get('is_export') + || $this->__get('is_func') || $this->__get('is_analyse')) + && (count($analyzed_sql[0]['select_expr']) == 0) + && isset($analyzed_sql[0]['queryflags']['select_from']) + && (count($analyzed_sql[0]['table_ref']) == 1); + } + + + /** + * 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 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, $onsubmit = '', + $input_for_real_end = '', $onclick = '' + ) { + + $caption_output = ''; + if (in_array( + $GLOBALS['cfg']['TableNavigationLinksMode'], + array('icons', 'both') + ) + ) { + $caption_output .= $caption; + } + + if (in_array( + $GLOBALS['cfg']['TableNavigationLinksMode'], + array('text', 'both') + ) + ) { + $caption_output .= ' ' . $title; + } + $title_output = ' title="' . $title . '"'; + + return '<td>' + . '<form action="sql.php" method="post" ' . $onsubmit . '>' + . PMA_generate_common_hidden_inputs( + $this->__get('db'), $this->__get('table') + ) + . '<input type="hidden" name="sql_query" value="' + . $html_sql_query . '" />' + . '<input type="hidden" name="pos" value="' . $pos . '" />' + . '<input type="hidden" name="goto" value="' . $this->__get('goto') + . '" />' + . $input_for_real_end + . '<input type="submit" name="navig"' + . ' class="ajax" ' + . 'value="' . $caption_output . '" ' . $title_output . $onclick . ' />' + . '</form>' + . '</td>'; + + } // end function _getTableNavigationButton() + + + /** + * Get a navigation bar to browse among the results of a SQL query + * + * @param integer $pos_next the offset for the "next" page + * @param integer $pos_prev the offset for the "previous" page + * @param string $id_for_direction_dropdown the id for the direction dropdown + * @param boolean $is_innodb whether its InnoDB or not + * + * @return string html content + * + * @access private + * + * @see _getTable() + */ + private function _getTableNavigation( + $pos_next, $pos_prev, $id_for_direction_dropdown, $is_innodb + ) { + + $table_navigation_html = ''; + $showtable = $this->__get('showtable'); // To use in isset + + // here, using htmlentities() would cause problems if the query + // contains accented characters + $html_sql_query = htmlspecialchars($this->__get('sql_query')); + + /** + * @todo move this to a central place + * @todo for other future table types + */ + $is_innodb = (isset($showtable['Type']) + && $showtable['Type'] == self::TABLE_TYPE_INNO_DB); + + // Navigation bar + $table_navigation_html .= '<table class="navigation nospacing nopadding">' + . '<tr>' + . '<td class="navigation_separator"></td>'; + + // Move to the beginning or to the previous page + if ($_SESSION['tmp_user_values']['pos'] + && ($_SESSION['tmp_user_values']['max_rows'] != self::ALL_ROWS) + ) { + + $table_navigation_html + .= $this->_getMoveBackwardButtonsForTableNavigation( + $html_sql_query, $pos_prev + ); + + } // end move back + + $nbTotalPage = 1; + //page redirection + // (unless we are showing all records) + if ($_SESSION['tmp_user_values']['max_rows'] != self::ALL_ROWS) { //if1 + + $pageNow = @floor( + $_SESSION['tmp_user_values']['pos'] + / $_SESSION['tmp_user_values']['max_rows'] + ) + 1; + + $nbTotalPage = @ceil( + $this->__get('unlim_num_rows') + / $_SESSION['tmp_user_values']['max_rows'] + ); + + if ($nbTotalPage > 1) { //if2 + + $table_navigation_html .= '<td>'; + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $this->__get('sql_query'), + 'goto' => $this->__get('goto'), + ); + + //<form> to keep the form alignment of button < and << + // and also to know what to execute when the selector changes + $table_navigation_html .= '<form action="sql.php' + . PMA_generate_common_url($_url_params) + . '" method="post">'; + + $table_navigation_html .= PMA_Util::pageselector( + 'pos', + $_SESSION['tmp_user_values']['max_rows'], + $pageNow, $nbTotalPage, 200, 5, 5, 20, 10 + ); + + $table_navigation_html .= '</form>' + . '</td>'; + } //_if2 + } //_if1 + + // Display the "Show all" button if allowed + if (($this->__get('num_rows') < $this->__get('unlim_num_rows')) + && ($GLOBALS['cfg']['ShowAll'] + || ($GLOBALS['cfg']['MaxRows'] * 5 >= $this->__get('unlim_num_rows'))) + ) { + + $table_navigation_html .= $this->_getShowAllButtonForTableNavigation( + $html_sql_query + ); + + } // end show all + + // Move to the next page or to the last one + $endpos = $_SESSION['tmp_user_values']['pos'] + + $_SESSION['tmp_user_values']['max_rows']; + + if (($endpos < $this->__get('unlim_num_rows')) + && ($this->__get('num_rows') >= $_SESSION['tmp_user_values']['max_rows']) + && ($_SESSION['tmp_user_values']['max_rows'] != self::ALL_ROWS) + ) { + + $table_navigation_html + .= $this->_getMoveForwardButtonsForTableNavigation( + $html_sql_query, $pos_next, $is_innodb + ); + + } // end move toward + + // show separator if pagination happen + if ($nbTotalPage > 1) { + $table_navigation_html + .= '<td><div class="navigation_separator">|</div></td>'; + } + + $table_navigation_html .= '<td>' + . '<div class="save_edited hide">' + . '<input type="submit" value="' . __('Save edited data') . '" />' + . '<div class="navigation_separator">|</div>' + . '</div>' + . '</td>' + . '<td>' + . '<div class="restore_column hide">' + . '<input type="submit" value="' . __('Restore column order') . '" />' + . '<div class="navigation_separator">|</div>' + . '</div>' + . '</td>'; + + // if displaying a VIEW, $unlim_num_rows could be zero because + // of $cfg['MaxExactCountViews']; in this case, avoid passing + // the 5th parameter to checkFormElementInRange() + // (this means we can't validate the upper limit + $table_navigation_html .= '<td class="navigation_goto">'; + + $table_navigation_html .= '<form action="sql.php" method="post" ' + . 'onsubmit="return ' + . '(checkFormElementInRange(' + . 'this, ' + . '\'session_max_rows\', ' + . '\'' + . str_replace('\'', '\\\'', __('%d is not valid row number.')) + . '\', ' + . '1)' + . ' && ' + . 'checkFormElementInRange(' + . 'this, ' + . '\'pos\', ' + . '\'' + . str_replace('\'', '\\\'', __('%d is not valid row number.')) + . '\', ' + . '0' + . (($this->__get('unlim_num_rows') > 0) + ? ', ' . ($this->__get('unlim_num_rows') - 1) + : '' + ) + . ')' + . ')' + .'">'; + + $table_navigation_html .= PMA_generate_common_hidden_inputs( + $this->__get('db'), $this->__get('table') + ); + + $table_navigation_html .= $this->_getAdditionalFieldsForTableNavigation( + $html_sql_query, $pos_next, $id_for_direction_dropdown + ); + + $table_navigation_html .= '</form>' + . '</td>' + . '<td class="navigation_separator"></td>' + . '</tr>' + . '</table>'; + + return $table_navigation_html; + + } // end of the '_getTableNavigation()' function + + + /** + * 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 + ) + . $this->_getTableNavigationButton( + '<', _pgettext('Previous page', 'Previous'), $pos_prev, + $html_sql_query + ); + } // end of the '_getMoveBackwardButtonsForTableNavigation()' function + + + /** + * Prepare Show All button for table navigation + * + * @param string $html_sql_query the sql encoded by html special characters + * + * @return string html content + * + * @access private + * + * @see _getTableNavigation() + */ + private function _getShowAllButtonForTableNavigation($html_sql_query) + { + return "\n" + . '<td>' + . '<form action="sql.php" method="post">' + . PMA_generate_common_hidden_inputs( + $this->__get('db'), $this->__get('table') + ) + . '<input type="hidden" name="sql_query" value="' + . $html_sql_query . '" />' + . '<input type="hidden" name="pos" value="0" />' + . '<input type="hidden" name="session_max_rows" value="all" />' + . '<input type="hidden" name="goto" value="' . $this->__get('goto') + . '" />' + . '<input type="submit" name="navig" value="' . __('Show all') . '" />' + . '</form>' + . '</td>'; + } // end of the '_getShowAllButtonForTableNavigation()' function + + + /** + * Prepare move farward buttons - next and last + * + * @param string $html_sql_query the sql encoded by html special characters + * @param integer $pos_next the offset for the "next" page + * @param boolean $is_innodb whether its InnoDB or not + * + * @return string $buttons_html 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 + ); + + // 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 = ''; + } + + $onsubmit = 'onsubmit="return ' + . ($_SESSION['tmp_user_values']['pos'] + + $_SESSION['tmp_user_values']['max_rows'] + < $this->__get('unlim_num_rows') + && $this->__get('num_rows') >= $_SESSION['tmp_user_values']['max_rows']) + ? 'true' + : 'false' . '"'; + + // display the End button + $buttons_html .= $this->_getTableNavigationButton( + '>>', + _pgettext('Last page', 'End'), + @((ceil( + $this->__get('unlim_num_rows') + / $_SESSION['tmp_user_values']['max_rows'] + )- 1) * $_SESSION['tmp_user_values']['max_rows']), + $html_sql_query, $onsubmit, $input_for_real_end, $onclick + ); + + return $buttons_html; + + } // end of the '_getMoveForwardButtonsForTableNavigation()' function + + + /** + * Prepare feilds followed by Show button for table navigation + * Start row, Number of rows, Headers every + * + * @param string $html_sql_query the sql encoded by html special + * characters + * @param integer $pos_next the offset for the "next" page + * @param string $id_for_direction_dropdown the id for the direction dropdown + * + * @return string $additional_fields_html html content + * + * @access private + * + * @see _getTableNavigation() + */ + private function _getAdditionalFieldsForTableNavigation( + $html_sql_query, $pos_next, $id_for_direction_dropdown + ) { + + $additional_fields_html = ''; + + $additional_fields_html .= '<input type="hidden" name="sql_query" ' + . 'value="' . $html_sql_query . '" />' + . '<input type="hidden" name="goto" value="' . $this->__get('goto') + . '" />' + . '<input type="submit" name="navig"' + . ' class="ajax"' + . ' value="' . __('Show') . ' :" />' + . __('Start row') . ': ' . "\n" + . '<input type="text" name="pos" size="3" value="' + . (($pos_next >= $this->__get('unlim_num_rows')) ? 0 : $pos_next) + . '" class="textfield" onfocus="this.select()" />' + . __('Number of rows') . ': ' . "\n" + . '<input type="text" name="session_max_rows" size="3" value="' + . (($_SESSION['tmp_user_values']['max_rows'] != self::ALL_ROWS) + ? $_SESSION['tmp_user_values']['max_rows'] + : $GLOBALS['cfg']['MaxRows']) + . '" class="textfield" onfocus="this.select()" />'; + + if ($GLOBALS['cfg']['ShowDisplayDirection']) { + // Display mode (horizontal/vertical and repeat headers) + $additional_fields_html .= __('Mode') . ': ' . "\n"; + $choices = array( + 'horizontal' => __('horizontal'), + 'horizontalflipped' => __('horizontal (rotated headers)'), + 'vertical' => __('vertical') + ); + + $additional_fields_html .= PMA_Util::getDropdown( + 'disp_direction', $choices, + $_SESSION['tmp_user_values']['disp_direction'], + $id_for_direction_dropdown + ); + unset($choices); + } + + $additional_fields_html .= sprintf( + __('Headers every %s rows'), + '<input type="text" size="3" name="repeat_cells" value="' + . $_SESSION['tmp_user_values']['repeat_cells'] + . '" class="textfield" /> ' + ); + + return $additional_fields_html; + + } // end of the '_getAdditionalFieldsForTableNavigation()' function + + + /** + * Get the headers of the results table + * + * @param array &$is_display which elements to display + * @param array $analyzed_sql the analyzed query + * @param string $sort_expression sort expression + * @param string $sort_expression_nodirection sort expression without direction + * @param string $sort_direction sort direction + * @param boolean $is_limited_display with limited operations or not + * + * @return string html content + * + * @access private + * + * @see getTable() + */ + private function _getTableHeaders( + &$is_display, $analyzed_sql = '', + $sort_expression = '', $sort_expression_nodirection = '', + $sort_direction = '', $is_limited_display = false + ) { + + $table_headers_html = ''; + // Following variable are needed for use in isset/empty or + // use with array indexes/safe use in foreach + $fields_meta = $this->__get('fields_meta'); + $highlight_columns = $this->__get('highlight_columns'); + $printview = $this->__get('printview'); + $vertical_display = $this->__get('vertical_display'); + + // required to generate sort links that will remember whether the + // "Show all" button has been clicked + $sql_md5 = md5($this->__get('sql_query')); + $session_max_rows = $is_limited_display + ? 0 + : $_SESSION['tmp_user_values']['query'][$sql_md5]['max_rows']; + + $direction = isset($_SESSION['tmp_user_values']['disp_direction']) + ? $_SESSION['tmp_user_values']['disp_direction'] + : ''; + + if ($analyzed_sql == '') { + $analyzed_sql = array(); + } + + $directionCondition = ($direction == self::DISP_DIR_HORIZONTAL) + || ($direction == self::DISP_DIR_HORIZONTAL_FLIPPED); + + // can the result be sorted? + if ($is_display['sort_lnk'] == '1') { + + list($unsorted_sql_query, $drop_down_html) + = $this->_getUnsortedSqlAndSortByKeyDropDown( + $analyzed_sql, $sort_expression + ); + + $table_headers_html .= $drop_down_html; + + } + + // Output data needed for grid editing + $table_headers_html .= '<input id="save_cells_at_once" type="hidden" value="' + . $GLOBALS['cfg']['SaveCellsAtOnce'] . '" />' + . '<div class="common_hidden_inputs">' + . PMA_generate_common_hidden_inputs( + $this->__get('db'), $this->__get('table') + ) + . '</div>'; + + // Output data needed for column reordering and show/hide column + if ($this->_isSelect($analyzed_sql)) { + $table_headers_html .= $this->_getDataForResettingColumnOrder(); + } + + $vertical_display['emptypre'] = 0; + $vertical_display['emptyafter'] = 0; + $vertical_display['textbtn'] = ''; + $full_or_partial_text_link = null; + + $this->__set('vertical_display', $vertical_display); + + // Display options (if we are not in print view) + if (! (isset($printview) && ($printview == '1')) && ! $is_limited_display) { + + $table_headers_html .= $this->_getOptionsBlock(); + + // prepare full/partial text button or link + $full_or_partial_text_link = $this->_getFullOrPartialTextButtonOrLink(); + } + + // Start of form for multi-rows edit/delete/export + $table_headers_html .= $this->_getFormForMultiRowOperations( + $is_display['del_lnk'] + ); + + // 1. Set $colspan or $rowspan and generate html with full/partial + // text button or link + list($colspan, $rowspan, $button_html) + = $this->_getFieldVisibilityParams( + $directionCondition, $is_display, $full_or_partial_text_link + ); + + $table_headers_html .= $button_html; + + // 2. Displays the fields' name + // 2.0 If sorting links should be used, checks if the query is a "JOIN" + // statement (see 2.1.3) + + // 2.0.1 Prepare Display column comments if enabled + // ($GLOBALS['cfg']['ShowBrowseComments']). + // Do not show comments, if using horizontalflipped mode, + // because of space usage + $comments_map = $this->_getTableCommentsArray($direction, $analyzed_sql); + + if ($GLOBALS['cfgRelation']['commwork'] + && $GLOBALS['cfgRelation']['mimework'] + && $GLOBALS['cfg']['BrowseMIME'] + && ! $_SESSION['tmp_user_values']['hide_transformation'] + ) { + include_once './libraries/transformations.lib.php'; + $this->__set( + 'mime_map', + PMA_getMIME($this->__get('db'), $this->__get('table')) + ); + } + + // See if we have to highlight any header fields of a WHERE query. + // Uses SQL-Parser results. + $this->_setHighlightedColumnGlobalField($analyzed_sql); + + list($col_order, $col_visib) = $this->_getColumnParams($analyzed_sql); + + for ($j = 0; $j < $this->__get('fields_cnt'); $j++) { + + // assign $i with appropriate column order + $i = $col_order ? $col_order[$j] : $j; + + // See if this column should get highlight because it's used in the + // where-query. + $condition_field = (isset($highlight_columns[$fields_meta[$i]->name]) + || isset($highlight_columns[PMA_Util::backquote($fields_meta[$i]->name)])) + ? true + : false; + + // 2.0 Prepare comment-HTML-wrappers for each row, if defined/enabled. + $comments = $this->_getCommentForRow($comments_map, $fields_meta[$i]); + + $vertical_display = $this->__get('vertical_display'); + + if (($is_display['sort_lnk'] == '1') && ! $is_limited_display) { + + list($order_link, $sorted_headrer_html) + = $this->_getOrderLinkAndSortedHeaderHtml( + $fields_meta[$i], $sort_expression, + $sort_expression_nodirection, $i, $unsorted_sql_query, + $session_max_rows, $direction, $comments, + $sort_direction, $directionCondition, $col_visib, + $col_visib[$j], $condition_field + ); + + $table_headers_html .= $sorted_headrer_html; + + $vertical_display['desc'][] = ' <th ' + . 'class="draggable' + . ($condition_field ? ' condition' : '') + . '" data-column="' . htmlspecialchars($fields_meta[$i]->name) + . '">' . "\n" . $order_link . $comments . ' </th>' . "\n"; + } else { + // 2.2 Results can't be sorted + + if ($directionCondition) { + $table_headers_html + .= $this->_getDraggableClassForNonSortableColumns( + $col_visib, $col_visib[$j], $condition_field, + $direction, $fields_meta[$i], $comments + ); + } + + $vertical_display['desc'][] = ' <th ' + . 'class="draggable' + . ($condition_field ? ' condition"' : '') + . '" data-column="' . htmlspecialchars($fields_meta[$i]->name) + . '">' . "\n" . ' ' + . htmlspecialchars($fields_meta[$i]->name) + . "\n" . $comments . ' </th>'; + } // end else (2.2) + + $this->__set('vertical_display', $vertical_display); + + } // end for + + // Display column at rightside - checkboxes or empty column + $table_headers_html .= $this->_getColumnAtRightSide( + $is_display, $directionCondition, $full_or_partial_text_link, + $colspan, $rowspan + ); + + if ($directionCondition) { + $table_headers_html .= '</tr>' + . '</thead>'; + } + + return $table_headers_html; + + } // end of the '_getTableHeaders()' function + + + /** + * Prepare unsorted sql query and sort by key drop down + * + * @param array $analyzed_sql the analyzed query + * @param string $sort_expression sort expression + * + * @return array two element array - $unsorted_sql_query, $drop_down_html + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getUnsortedSqlAndSortByKeyDropDown( + $analyzed_sql, $sort_expression + ) { + + $drop_down_html = ''; + + // Just as fallback + $unsorted_sql_query = $this->__get('sql_query'); + if (isset($analyzed_sql[0]['unsorted_query'])) { + $unsorted_sql_query = $analyzed_sql[0]['unsorted_query']; + } + // Handles the case of multiple clicks on a column's header + // which would add many spaces before "ORDER BY" in the + // generated query. + $unsorted_sql_query = trim($unsorted_sql_query); + + // sorting by indexes, only if it makes sense (only one table ref) + if (isset($analyzed_sql) + && isset($analyzed_sql[0]) + && isset($analyzed_sql[0]['querytype']) + && ($analyzed_sql[0]['querytype'] == self::QUERY_TYPE_SELECT) + && isset($analyzed_sql[0]['table_ref']) + && (count($analyzed_sql[0]['table_ref']) == 1) + ) { + // grab indexes data: + $indexes = PMA_Index::getFromTable( + $this->__get('table'), + $this->__get('db') + ); + + // do we have any index? + if ($indexes) { + $drop_down_html = $this->_getSortByKeyDropDown( + $indexes, $sort_expression, + $unsorted_sql_query + ); + } + } + + return array($unsorted_sql_query, $drop_down_html); + + } // end of the '_getUnsortedSqlAndSortByKeyDropDown()' function + + + /** + * Prepare sort by key dropdown - html code segment + * + * @param array $indexes the indexes of the table for sort criteria + * @param string $sort_expression the sort expression + * @param string $unsorted_sql_query the unsorted sql query + * + * @return string $drop_down_html html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getSortByKeyDropDown( + $indexes, $sort_expression, $unsorted_sql_query + ) { + + $drop_down_html = ''; + + $drop_down_html .= '<form action="sql.php" method="post">' . "\n" + . PMA_generate_common_hidden_inputs( + $this->__get('db'), $this->__get('table') + ) + . __('Sort by key') + . ': <select name="sql_query" class="autosubmit">' . "\n"; + + $used_index = false; + $local_order = (isset($sort_expression) ? $sort_expression : ''); + + foreach ($indexes as $index) { + + $asc_sort = '`' + . implode('` ASC, `', array_keys($index->getColumns())) + . '` ASC'; + + $desc_sort = '`' + . implode('` DESC, `', array_keys($index->getColumns())) + . '` DESC'; + + $used_index = $used_index + || ($local_order == $asc_sort) + || ($local_order == $desc_sort); + + if (preg_match( + '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|' + . 'FOR UPDATE|LOCK IN SHARE MODE))@is', + $unsorted_sql_query, $my_reg + )) { + $unsorted_sql_query_first_part = $my_reg[1]; + $unsorted_sql_query_second_part = $my_reg[2]; + } else { + $unsorted_sql_query_first_part = $unsorted_sql_query; + $unsorted_sql_query_second_part = ''; + } + + $drop_down_html .= '<option value="' + . htmlspecialchars( + $unsorted_sql_query_first_part . "\n" + . ' ORDER BY ' . $asc_sort + . $unsorted_sql_query_second_part + ) + . '"' . ($local_order == $asc_sort + ? ' selected="selected"' + : '') + . '>' . htmlspecialchars($index->getName()) . ' (' + . __('Ascending') . ')</option>'; + + $drop_down_html .= '<option value="' + . htmlspecialchars( + $unsorted_sql_query_first_part . "\n" + . ' ORDER BY ' . $desc_sort + . $unsorted_sql_query_second_part + ) + . '"' . ($local_order == $desc_sort + ? ' selected="selected"' + : '') + . '>' . htmlspecialchars($index->getName()) . ' (' + . __('Descending') . ')</option>'; + } + + $drop_down_html .= '<option value="' . htmlspecialchars($unsorted_sql_query) + . '"' . ($used_index ? '' : ' selected="selected"') . '>' . __('None') + . '</option>' + . '</select>' . "\n" + . '</form>' . "\n"; + + return $drop_down_html; + + } // end of the '_getSortByKeyDropDown()' function + + + /** + * Set column span, row span and prepare html with full/partial + * text button or link + * + * @param boolean $directionCondition display direction horizontal or + * horizontalflipped + * @param array &$is_display which elements to display + * @param string $full_or_partial_text_link full/partial link or text button + * + * @return array 3 element array - $colspan, $rowspan, $button_html + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getFieldVisibilityParams( + $directionCondition, &$is_display, $full_or_partial_text_link + ) { + + $button_html = ''; + $colspan = $rowspan = null; + $vertical_display = $this->__get('vertical_display'); + + // 1. Displays the full/partial text button (part 1)... + if ($directionCondition) { + + $button_html .= '<thead><tr>' . "\n"; + + $colspan = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) + ? ' colspan="4"' + : ''; + + } else { + $rowspan = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) + ? ' rowspan="4"' + : ''; + } + + // ... before the result table + if ((($is_display['edit_lnk'] == self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] == self::NO_EDIT_OR_DELETE)) + && ($is_display['text_btn'] == '1') + ) { + + $vertical_display['emptypre'] + = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0; + + if ($directionCondition) { + + $button_html .= '<th colspan="' . $this->__get('fields_cnt') . '">' + . '</th>' + . '</tr>' + . '<tr>'; + + // end horizontal/horizontalflipped mode + } else { + + $span = $this->__get('num_rows') + 1 + floor( + $this->__get('num_rows') + / $_SESSION['tmp_user_values']['repeat_cells'] + ); + $button_html .= '<tr><th colspan="' . $span . '"></th></tr>'; + + } // end vertical mode + + } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && ($is_display['text_btn'] == '1') + ) { + // ... at the left column of the result table header if possible + // and required + + $vertical_display['emptypre'] + = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0; + + if ($directionCondition) { + + $button_html .= '<th ' . $colspan . '>' + . $full_or_partial_text_link . '</th>'; + // end horizontal/horizontalflipped mode + + } else { + + $vertical_display['textbtn'] + = ' <th ' . $rowspan . ' class="vmiddle">' . "\n" + . ' ' . "\n" + . ' </th>' . "\n"; + } // end vertical mode + + } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + || ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) + ) { + // ... elseif no button, displays empty(ies) col(s) if required + + $vertical_display['emptypre'] + = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 0; + + if ($directionCondition) { + + $button_html .= '<td ' . $colspan . '></td>'; + + // end horizontal/horizontalfipped mode + } else { + $vertical_display['textbtn'] = ' <td' . $rowspan . + '></td>' . "\n"; + } // end vertical mode + + } elseif (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) + && ($directionCondition) + ) { + // ... elseif display an empty column if the actions links are + // disabled to match the rest of the table + $button_html .= '<th></th>'; + } + + $this->__set('vertical_display', $vertical_display); + + return array($colspan, $rowspan, $button_html); + + } // end of the '_getFieldVisibilityParams()' function + + + /** + * Get table comments as array + * + * @param boolean $direction display direction, horizontal + * or horizontalflipped + * @param array $analyzed_sql the analyzed query + * + * @return array $comments_map table comments when condition true + * null when condition falls + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getTableCommentsArray($direction, $analyzed_sql) + { + + $comments_map = null; + + if ($GLOBALS['cfg']['ShowBrowseComments'] + && ($direction != self::DISP_DIR_HORIZONTAL_FLIPPED) + ) { + $comments_map = array(); + if (isset($analyzed_sql[0]) + && is_array($analyzed_sql[0]) + && isset($analyzed_sql[0]['table_ref']) + ) { + foreach ($analyzed_sql[0]['table_ref'] as $tbl) { + $tb = $tbl['table_true_name']; + $comments_map[$tb] = PMA_getComments($this->__get('db'), $tb); + unset($tb); + } + } + } + + return $comments_map; + + } // end of the '_getTableCommentsArray()' function + + + /** + * Set global array for store highlighted header fields + * + * @param array $analyzed_sql the analyzed query + * + * @return void + * + * @access private + * + * @see _getTableHeaders() + */ + private function _setHighlightedColumnGlobalField($analyzed_sql) + { + + $highlight_columns = array(); + if (isset($analyzed_sql) && isset($analyzed_sql[0]) + && isset($analyzed_sql[0]['where_clause_identifiers']) + ) { + + $wi = 0; + if (isset($analyzed_sql[0]['where_clause_identifiers']) + && is_array($analyzed_sql[0]['where_clause_identifiers']) + ) { + foreach ($analyzed_sql[0]['where_clause_identifiers'] + as $wci_nr => $wci + ) { + $highlight_columns[$wci] = 'true'; + } + } + } + + $this->__set('highlight_columns', $highlight_columns); + + } // end of the '_setHighlightedColumnGlobalField()' function + + + /** + * Prepare data for column restoring and show/hide + * + * @return string $data_html html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getDataForResettingColumnOrder() + { + + $data_html = ''; + + // generate the column order, if it is set + $pmatable = new PMA_Table($this->__get('table'), $this->__get('db')); + $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER); + + if ($col_order) { + $data_html .= '<input id="col_order" type="hidden" value="' + . implode(',', $col_order) . '" />'; + } + + $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB); + + if ($col_visib) { + $data_html .= '<input id="col_visib" type="hidden" value="' + . implode(',', $col_visib) . '" />'; + } + + // generate table create time + if (! PMA_Table::isView($this->__get('db'), $this->__get('table'))) { + $data_html .= '<input id="table_create_time" type="hidden" value="' + . PMA_Table::sGetStatusInfo( + $this->__get('db'), $this->__get('table'), 'Create_time' + ) . '" />'; + } + + return $data_html; + + } // end of the '_getDataForResettingColumnOrder()' function + + + /** + * Prepare option fields block + * + * @return string $options_html html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getOptionsBlock() + { + + $options_html = ''; + + $options_html .= '<form method="post" action="sql.php" ' + . 'name="displayOptionsForm" ' + . 'id="displayOptionsForm"'; + + $options_html .= ' class="ajax" '; + + $options_html .= '>'; + $url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $this->__get('sql_query'), + 'goto' => $this->__get('goto'), + 'display_options_form' => 1 + ); + + $options_html .= PMA_generate_common_hidden_inputs($url_params) + . '<br />' + . PMA_Util::getDivForSliderEffect( + 'displayoptions', __('Options') + ) + . '<fieldset>'; + + $options_html .= '<div class="formelement">'; + $choices = array( + 'P' => __('Partial texts'), + 'F' => __('Full texts') + ); + + $options_html .= PMA_Util::getRadioFields( + 'display_text', $choices, + $_SESSION['tmp_user_values']['display_text'] + ) + . '</div>'; + + if ($GLOBALS['cfgRelation']['relwork'] + && $GLOBALS['cfgRelation']['displaywork'] + ) { + $options_html .= '<div class="formelement">'; + $choices = array( + 'K' => __('Relational key'), + 'D' => __('Relational display column') + ); + + $options_html .= PMA_Util::getRadioFields( + 'relational_display', $choices, + $_SESSION['tmp_user_values']['relational_display'] + ) + . '</div>'; + } + + $options_html .= '<div class="formelement">' + . PMA_Util::getCheckbox( + 'display_binary', __('Show binary contents'), + ! empty($_SESSION['tmp_user_values']['display_binary']), false + ) + . '<br />' + . PMA_Util::getCheckbox( + 'display_blob', __('Show BLOB contents'), + ! empty($_SESSION['tmp_user_values']['display_blob']), false + ) + . '<br />' + . PMA_Util::getCheckbox( + 'display_binary_as_hex', __('Show binary contents as HEX'), + ! empty($_SESSION['tmp_user_values']['display_binary_as_hex']), false + ) + . '</div>'; + + // I would have preferred to name this "display_transformation". + // This is the only way I found to be able to keep this setting sticky + // per SQL query, and at the same time have a default that displays + // the transformations. + $options_html .= '<div class="formelement">' + . PMA_Util::getCheckbox( + 'hide_transformation', __('Hide browser transformation'), + ! empty($_SESSION['tmp_user_values']['hide_transformation']), false + ) + . '</div>'; + + if (! PMA_DRIZZLE) { + $options_html .= '<div class="formelement">'; + $choices = array( + 'GEOM' => __('Geometry'), + 'WKT' => __('Well Known Text'), + 'WKB' => __('Well Known Binary') + ); + + $options_html .= PMA_Util::getRadioFields( + 'geometry_display', $choices, + $_SESSION['tmp_user_values']['geometry_display'] + ) + . '</div>'; + } + + $options_html .= '<div class="clearfloat"></div>' + . '</fieldset>'; + + $options_html .= '<fieldset class="tblFooters">' + . '<input type="submit" value="' . __('Go') . '" />' + . '</fieldset>' + . '</div>' + . '</form>'; + + return $options_html; + + } // end of the '_getOptionsBlock()' function + + + /** + * Get full/partial text button or link + * + * @return string html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getFullOrPartialTextButtonOrLink() + { + + $url_params_full_text = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $this->__get('sql_query'), + 'goto' => $this->__get('goto'), + 'full_text_button' => 1 + ); + + if ($_SESSION['tmp_user_values']['display_text'] == self::DISPLAY_FULL_TEXT) { + // currently in fulltext mode so show the opposite link + $tmp_image_file = $this->__get('pma_theme_image') . 's_partialtext.png'; + $tmp_txt = __('Partial texts'); + $url_params_full_text['display_text'] = self::DISPLAY_PARTIAL_TEXT; + } else { + $tmp_image_file = $this->__get('pma_theme_image') . 's_fulltext.png'; + $tmp_txt = __('Full texts'); + $url_params_full_text['display_text'] = self::DISPLAY_FULL_TEXT; + } + + $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="' + . $tmp_txt . '" title="' . $tmp_txt . '" />'; + $tmp_url = 'sql.php' . PMA_generate_common_url($url_params_full_text); + + return PMA_Util::linkOrButton( + $tmp_url, $tmp_image, array(), false + ); + + } // end of the '_getFullOrPartialTextButtonOrLink()' function + + + /** + * Prepare html form for multi row operations + * + * @param string $del_lnk the delete link of current row + * + * @return string $form_html html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getFormForMultiRowOperations($del_lnk) + { + + $form_html = ''; + + if (($del_lnk == self::DELETE_ROW) || ($del_lnk == self::KILL_PROCESS)) { + + $form_html .= '<form method="post" action="tbl_row_action.php" ' + . 'name="resultsForm" id="resultsForm"'; + + $form_html .= ' class="ajax" '; + + $form_html .= '>' + . PMA_generate_common_hidden_inputs( + $this->__get('db'), $this->__get('table'), 1 + ) + . '<input type="hidden" name="goto" value="sql.php" />'; + } + + $form_html .= '<table id="table_results" class="data'; + $form_html .= ' ajax'; + $form_html .= '">'; + + return $form_html; + + } // end of the '_getFormForMultiRowOperations()' function + + + /** + * Get comment for row + * + * @param array $comments_map comments array + * @param array $fields_meta set of field properties + * + * @return string $comment html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getCommentForRow($comments_map, $fields_meta) + { + $comments = ''; + if (isset($comments_map) + && isset($comments_map[$fields_meta->table]) + && isset($comments_map[$fields_meta->table][$fields_meta->name]) + ) { + $comments = '<span class="tblcomment">' + . htmlspecialchars( + $comments_map[$fields_meta->table][$fields_meta->name] + ) + . '</span>'; + } + return $comments; + } // end of the '_getCommentForRow()' function + + + /** + * Prepare parameters and html for sorted table header fields + * + * @param array $fields_meta set of field properties + * @param string $sort_expression sort expression + * @param string $sort_expression_nodirection sort expression without direction + * @param integer $column_index the index of the column + * @param string $unsorted_sql_query the unsorted sql query + * @param integer $session_max_rows maximum rows resulted by sql + * @param string $direction the display direction + * @param string $comments comment for row + * @param string $sort_direction sort direction + * @param boolean $directionCondition display direction horizontal + * or horizontalflipped + * @param boolean $col_visib column is visible(false) + * array column isn't visible(string array) + * @param string $col_visib_j element of $col_visib array + * @param boolean $condition_field whether the column is a part of + * the where clause + * + * @return array 2 element array - $order_link, $sorted_header_html + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getOrderLinkAndSortedHeaderHtml( + $fields_meta, $sort_expression, $sort_expression_nodirection, + $column_index, $unsorted_sql_query, $session_max_rows, $direction, + $comments, $sort_direction, $directionCondition, $col_visib, + $col_visib_j, $condition_field + ) { + + $sorted_header_html = ''; + + // Checks if the table name is required; it's the case + // for a query with a "JOIN" statement and if the column + // isn't aliased, or in queries like + // SELECT `1`.`master_field` , `2`.`master_field` + // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2` + + $sort_tbl = (isset($fields_meta->table) + && strlen($fields_meta->table)) + ? PMA_Util::backquote( + $fields_meta->table + ) . '.' + : ''; + + // Checks if the current column is used to sort the + // results + // the orgname member does not exist for all MySQL versions + // but if found, it's the one on which to sort + $name_to_use_in_sort = $fields_meta->name; + $is_orgname = false; + if (isset($fields_meta->orgname) + && strlen($fields_meta->orgname) + ) { + $name_to_use_in_sort = $fields_meta->orgname; + $is_orgname = true; + } + + // $name_to_use_in_sort might contain a space due to + // formatting of function expressions like "COUNT(name )" + // so we remove the space in this situation + $name_to_use_in_sort = str_replace(' )', ')', $name_to_use_in_sort); + + $is_in_sort = $this->_isInSorted( + $sort_expression, $sort_expression_nodirection, + $sort_tbl, $name_to_use_in_sort + ); + + // Check the field name for a bracket. + // If it contains one, it's probably a function column + // like 'COUNT(`field`)' + // It still might be a column name of a view. See bug #3383711 + // Check is_orgname. + if ((strpos($name_to_use_in_sort, '(') !== false) && ! $is_orgname) { + $sort_order = "\n" . 'ORDER BY ' . $name_to_use_in_sort . ' '; + } else { + $sort_order = "\n" . 'ORDER BY ' . $sort_tbl + . PMA_Util::backquote( + $name_to_use_in_sort + ) . ' '; + } + unset($name_to_use_in_sort); + unset($is_orgname); + + // Do define the sorting URL + + list($sort_order, $order_img) = $this->_getSortingUrlParams( + $is_in_sort, $sort_direction, $fields_meta, + $sort_order, $column_index + ); + + if (preg_match( + '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|' + . 'LOCK IN SHARE MODE))@is', + $unsorted_sql_query, $regs3 + )) { + $sorted_sql_query = $regs3[1] . $sort_order . $regs3[2]; + } else { + $sorted_sql_query = $unsorted_sql_query . $sort_order; + } + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $sorted_sql_query, + 'session_max_rows' => $session_max_rows + ); + $order_url = 'sql.php' . PMA_generate_common_url($_url_params); + + // Displays the sorting URL + // enable sort order swapping for image + $order_link = $this->_getSortOrderLink( + $order_img, $column_index, $direction, + $fields_meta, $order_url + ); + + if ($directionCondition) { + $sorted_header_html .= $this->_getDraggableClassForSortableColumns( + $col_visib, $col_visib_j, $condition_field, $direction, + $fields_meta, $order_link, $comments + ); + } + + return array($order_link, $sorted_header_html); + + } // end of the '_getOrderLinkAndSortedHeaderHtml()' function + + + /** + * Check whether the column is sorted + * + * @param string $sort_expression sort expression + * @param string $sort_expression_nodirection sort expression without direction + * @param string $sort_tbl the table name + * @param string $name_to_use_in_sort the sorting column name + * + * @return boolean $is_in_sort the column sorted or not + * + * @access private + * + * @see _getTableHeaders() + */ + private function _isInSorted( + $sort_expression, $sort_expression_nodirection, $sort_tbl, + $name_to_use_in_sort + ) { + + if (empty($sort_expression)) { + $is_in_sort = false; + } else { + // Field name may be preceded by a space, or any number + // of characters followed by a dot (tablename.fieldname) + // so do a direct comparison for the sort expression; + // this avoids problems with queries like + // "SELECT id, count(id)..." and clicking to sort + // on id or on count(id). + // Another query to test this: + // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p + // (and try clicking on each column's header twice) + if (! empty($sort_tbl) + && strpos($sort_expression_nodirection, $sort_tbl) === false + && strpos($sort_expression_nodirection, '(') === false + ) { + $new_sort_expression_nodirection = $sort_tbl + . $sort_expression_nodirection; + } else { + $new_sort_expression_nodirection = $sort_expression_nodirection; + } + + $is_in_sort = false; + $sort_name = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort; + + if ($sort_name == str_replace('`', '', $new_sort_expression_nodirection) + || $sort_name == str_replace('`', '', $sort_expression_nodirection) + ) { + $is_in_sort = true; + } + } + + return $is_in_sort; + + } // end of the '_isInSorted()' function + + + /** + * Get sort url paramaeters - sort order and order image + * + * @param boolean $is_in_sort the column sorted or not + * @param string $sort_direction the sort direction + * @param array $fields_meta set of field properties + * @param string $sort_order the sorting order + * @param integer $column_index the index of the column + * + * @return array 2 element array - $sort_order, $order_img + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getSortingUrlParams( + $is_in_sort, $sort_direction, $fields_meta, $sort_order, $column_index + ) { + + if (! $is_in_sort) { + + // patch #455484 ("Smart" order) + $GLOBALS['cfg']['Order'] = strtoupper($GLOBALS['cfg']['Order']); + + if ($GLOBALS['cfg']['Order'] === self::SMART_SORT_ORDER) { + $sort_order .= (preg_match( + '@time|date@i', + $fields_meta->type + )) ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR; + } else { + $sort_order .= $GLOBALS['cfg']['Order']; + } + $order_img = ''; + + } elseif ($sort_direction == self::DESCENDING_SORT_DIR) { + + $sort_order .= ' ASC'; + $order_img = ' ' . PMA_Util::getImage( + 's_desc.png', __('Descending'), + array('class' => "soimg$column_index", 'title' => '') + ); + + $order_img .= ' ' . PMA_Util::getImage( + 's_asc.png', __('Ascending'), + array('class' => "soimg$column_index hide", 'title' => '') + ); + + } else { + + $sort_order .= ' DESC'; + $order_img = ' ' . PMA_Util::getImage( + 's_asc.png', __('Ascending'), + array('class' => "soimg$column_index", 'title' => '') + ); + + $order_img .= ' ' . PMA_Util::getImage( + 's_desc.png', __('Descending'), + array('class' => "soimg$column_index hide", 'title' => '') + ); + } + + return array($sort_order, $order_img); + + } // end of the '_getSortingUrlParams()' function + + + /** + * Get sort order link + * + * @param string $order_img the sort order image + * @param integer $col_index the index of the column + * @param string $direction the display direction + * @param array $fields_meta set of field properties + * @param string $order_url the url for sort + * + * @return string the sort order link + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getSortOrderLink( + $order_img, $col_index, $direction, $fields_meta, $order_url + ) { + + $order_link_params = array(); + + if (isset($order_img) && ($order_img != '')) { + if (strstr($order_img, 'asc')) { + $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()"; + $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()"; + } elseif (strstr($order_img, 'desc')) { + $order_link_params['onmouseover'] = "$('.soimg$col_index').toggle()"; + $order_link_params['onmouseout'] = "$('.soimg$col_index').toggle()"; + } + } + + if ($GLOBALS['cfg']['HeaderFlipType'] == self::HEADER_FLIP_TYPE_AUTO) { + + $GLOBALS['cfg']['HeaderFlipType'] + = (PMA_USR_BROWSER_AGENT == 'IE') + ? self::HEADER_FLIP_TYPE_CSS + : self::HEADER_FLIP_TYPE_FAKE; + } + + if ($direction == self::DISP_DIR_HORIZONTAL_FLIPPED + && $GLOBALS['cfg']['HeaderFlipType'] == self::HEADER_FLIP_TYPE_CSS + ) { + $order_link_params['style'] = 'direction: ltr; writing-mode: tb-rl;'; + } + + $order_link_content = (($direction == self::DISP_DIR_HORIZONTAL_FLIPPED) + && ($GLOBALS['cfg']['HeaderFlipType'] == self::HEADER_FLIP_TYPE_FAKE)) + ? PMA_Util::flipstring( + htmlspecialchars($fields_meta->name), + "<br />\n" + ) + : htmlspecialchars($fields_meta->name); + + return PMA_Util::linkOrButton( + $order_url, $order_link_content . $order_img, + $order_link_params, false, true + ); + + } // end of the '_getSortOrderLink()' function + + + /** + * Prepare columns to draggable effect for sortable columns + * + * @param boolean $col_visib the column is visible (false) + * array the column is not visible (string array) + * @param string $col_visib_j element of $col_visib array + * @param boolean $condition_field whether to add CSS class condition + * @param string $direction the display direction + * @param array $fields_meta set of field properties + * @param string $order_link the order link + * @param string $comments the comment for the column + * + * @return string $draggable_html html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getDraggableClassForSortableColumns( + $col_visib, $col_visib_j, $condition_field, $direction, $fields_meta, + $order_link, $comments + ) { + + $draggable_html = '<th'; + $th_class = array(); + $th_class[] = 'draggable'; + + if ($col_visib && !$col_visib_j) { + $th_class[] = 'hide'; + } + + if ($condition_field) { + $th_class[] = 'condition'; + } + + $th_class[] = 'column_heading'; + if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) { + $th_class[] = 'pointer'; + } + + if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) { + $th_class[] = 'marker'; + } + + $draggable_html .= ' class="' . implode(' ', $th_class); + + if ($direction == self::DISP_DIR_HORIZONTAL_FLIPPED) { + $draggable_html .= ' vbottom'; + } + + $draggable_html .= '" data-column="' . htmlspecialchars($fields_meta->name) + . '">' . $order_link . $comments . '</th>'; + + return $draggable_html; + + } // end of the '_getDraggableClassForSortableColumns()' function + + + /** + * Prepare columns to draggable effect for non sortable columns + * + * @param boolean $col_visib the column is visible (false) + * array the column is not visible (string array) + * @param string $col_visib_j element of $col_visib array + * @param boolean $condition_field whether to add CSS class condition + * @param string $direction the display direction + * @param array $fields_meta set of field properties + * @param string $comments the comment for the column + * + * @return string $draggable_html html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getDraggableClassForNonSortableColumns( + $col_visib, $col_visib_j, $condition_field, + $direction, $fields_meta, $comments + ) { + + $draggable_html = '<th'; + $th_class = array(); + $th_class[] = 'draggable'; + + if ($col_visib && !$col_visib_j) { + $th_class[] = 'hide'; + } + + if ($condition_field) { + $th_class[] = 'condition'; + } + + $draggable_html .= ' class="' . implode(' ', $th_class); + if ($direction == self::DISP_DIR_HORIZONTAL_FLIPPED) { + $draggable_html .= ' vbottom'; + } + + $draggable_html .= '"'; + if (($direction == self::DISP_DIR_HORIZONTAL_FLIPPED) + && ($GLOBALS['cfg']['HeaderFlipType'] == self::HEADER_FLIP_TYPE_CSS) + ) { + $draggable_html .= ' style="direction: ltr; writing-mode: tb-rl;"'; + } + + $draggable_html .= ' data-column="' + . htmlspecialchars($fields_meta->name) . '">'; + + if (($direction == self::DISP_DIR_HORIZONTAL_FLIPPED) + && ($GLOBALS['cfg']['HeaderFlipType'] == self::HEADER_FLIP_TYPE_FAKE) + ) { + + $draggable_html .= PMA_Util::flipstring( + htmlspecialchars($fields_meta->name), '<br />' + ); + + } else { + $draggable_html .= htmlspecialchars($fields_meta->name); + } + + $draggable_html .= "\n" . $comments . '</th>'; + + return $draggable_html; + + } // end of the '_getDraggableClassForNonSortableColumns()' function + + + /** + * Prepare column to show at right side - check boxes or empty column + * + * @param array &$is_display which elements to display + * @param boolean $directionCondition display direction horizontal + * or horizontalflipped + * @param string $full_or_partial_text_link full/partial link or text button + * @param string $colspan column span of table header + * @param string $rowspan row span of table header + * + * @return string html content + * + * @access private + * + * @see _getTableHeaders() + */ + private function _getColumnAtRightSide( + &$is_display, $directionCondition, $full_or_partial_text_link, + $colspan, $rowspan + ) { + + $right_column_html = ''; + $vertical_display = $this->__get('vertical_display'); + + // Displays the needed checkboxes at the right + // column of the result table header if possible and required... + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + || ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) + && ($is_display['text_btn'] == '1') + ) { + + $vertical_display['emptyafter'] + = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 1; + + if ($directionCondition) { + $right_column_html .= "\n" + . '<th ' . $colspan . '>' . $full_or_partial_text_link + . '</th>'; + + // end horizontal/horizontalflipped mode + } else { + $vertical_display['textbtn'] = ' <th ' . $rowspan + . ' class="vmiddle">' . "\n" + . ' ' . "\n" + . ' </th>' . "\n"; + } // end vertical mode + } elseif ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && (($is_display['edit_lnk'] == self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] == self::NO_EDIT_OR_DELETE)) + && (! isset($GLOBALS['is_header_sent']) || ! $GLOBALS['is_header_sent']) + ) { + // ... elseif no button, displays empty columns if required + // (unless coming from Browse mode print view) + + $vertical_display['emptyafter'] + = (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) ? 4 : 1; + + if ($directionCondition) { + $right_column_html .= "\n" + . '<td ' . $colspan . '></td>'; + + // end horizontal/horizontalflipped mode + } else { + $vertical_display['textbtn'] = ' <td' . $rowspan + . '></td>' . "\n"; + } // end vertical mode + } + + $this->__set('vertical_display', $vertical_display); + + return $right_column_html; + + } // end of the '_getColumnAtRightSide()' function + + + /** + * Prepares the display for a value + * + * @param string $class class of table cell + * @param bool $condition_field whether to add CSS class condition + * @param string $value value to display + * + * @return string the td + * + * @access private + * + * @see _getDataCellForBlobColumns(), _getDataCellForGeometryColumns(), + * _getDataCellForNonNumericAndNonBlobColumns() + */ + private function _buildValueDisplay($class, $condition_field, $value) + { + return '<td class="left ' . $class . ($condition_field ? ' condition' : '') + . '">' . $value . '</td>'; + } // end of the '_buildValueDisplay()' function + + + /** + * Prepares the display for a null value + * + * @param string $class class of table cell + * @param bool $condition_field whether to add CSS class condition + * @param object $meta the meta-information about this field + * @param string $align cell allignment + * + * @return string the td + * + * @access private + * + * @see _getDataCellForNumericColumns(), _getDataCellForBlobColumns(), + * _getDataCellForGeometryColumns(), + * _getDataCellForNonNumericAndNonBlobColumns() + */ + private function _buildNullDisplay($class, $condition_field, $meta, $align = '') + { + // the null class is needed for grid editing + return '<td ' . $align . ' class="' + . $this->_addClass( + $class, $condition_field, $meta, '' + ) + . ' null"><i>NULL</i></td>'; + } // end of the '_buildNullDisplay()' function + + + /** + * Prepares the display for an empty value + * + * @param string $class class of table cell + * @param bool $condition_field whether to add CSS class condition + * @param object $meta the meta-information about this field + * @param string $align cell allignment + * + * @return string the td + * + * @access private + * + * @see _getDataCellForNumericColumns(), _getDataCellForBlobColumns(), + * _getDataCellForGeometryColumns(), + * _getDataCellForNonNumericAndNonBlobColumns() + */ + private function _buildEmptyDisplay($class, $condition_field, $meta, $align = '') + { + return '<td ' . $align . ' class="' + . $this->_addClass( + $class, $condition_field, $meta, ' nowrap' + ) + . '"></td>'; + } // end of the '_buildEmptyDisplay()' function + + + /** + * Adds the relavant classes. + * + * @param string $class class of table cell + * @param bool $condition_field whether to add CSS class condition + * @param object $meta the meta-information about the field + * @param string $nowrap avoid wrapping + * @param bool $is_field_truncated is field truncated (display ...) + * @param string $transformation_plugin transformation plugin. + * Can also be the default function: + * PMA_mimeDefaultFunction + * @param string $default_function default transformation function + * + * @return string the list of classes + * + * @access private + * + * @see _buildNullDisplay(), _getRowData() + */ + private function _addClass( + $class, $condition_field, $meta, $nowrap, $is_field_truncated = false, + $transformation_plugin = '', $default_function = '' + ) { + + // Define classes to be added to this data field based on the type of data + $enum_class = ''; + if (strpos($meta->flags, 'enum') !== false) { + $enum_class = ' enum'; + } + + $set_class = ''; + if (strpos($meta->flags, 'set') !== false) { + $set_class = ' set'; + } + + $bit_class = ''; + if (strpos($meta->type, 'bit') !== false) { + $bit_class = ' bit'; + } + + $mime_type_class = ''; + if (isset($meta->mimetype)) { + $mime_type_class = ' ' . preg_replace('/\//', '_', $meta->mimetype); + } + + return $class . ($condition_field ? ' condition' : '') . $nowrap + . ' ' . ($is_field_truncated ? ' truncated' : '') + . ($transformation_plugin != $default_function ? ' transformed' : '') + . $enum_class . $set_class . $bit_class . $mime_type_class; + + } // end of the '_addClass()' function + + + /** + * Prepare the body of the results table + * + * @param integer &$dt_result the link id associated to the query + * which results have to be displayed + * @param array &$is_display which elements to display + * @param array $map the list of relations + * @param array $analyzed_sql the analyzed query + * @param boolean $is_limited_display with limited operations or not + * + * @return string $table_body_html html content + * + * @global array $row current row data + * + * @access private + * + * @see getTable() + */ + private function _getTableBody( + &$dt_result, &$is_display, $map, $analyzed_sql, $is_limited_display = false + ) { + + global $row; // mostly because of browser transformations, + // to make the row-data accessible in a plugin + + $table_body_html = ''; + + // query without conditions to shorten URLs when needed, 200 is just + // guess, it should depend on remaining URL length + $url_sql_query = $this->_getUrlSqlQuery($analyzed_sql); + + $vertical_display = $this->__get('vertical_display'); + + if (! is_array($map)) { + $map = array(); + } + + $row_no = 0; + $vertical_display['edit'] = array(); + $vertical_display['copy'] = array(); + $vertical_display['delete'] = array(); + $vertical_display['data'] = array(); + $vertical_display['row_delete'] = array(); + $this->__set('vertical_display', $vertical_display); + + // name of the class added to all grid editable elements; + // if we don't have all the columns of a unique key in the result set, + // do not permit grid editing + if ($is_limited_display || ! $this->__get('editable')) { + $grid_edit_class = ''; + } else { + switch ($GLOBALS['cfg']['GridEditing']) { + case 'double-click': + // trying to reduce generated HTML by using shorter + // classes like click1 and click2 + $grid_edit_class = 'grid_edit click2'; + break; + case 'click': + $grid_edit_class = 'grid_edit click1'; + break; + case 'disabled': + $grid_edit_class = ''; + break; + } + } + + // prepare to get the column order, if available + list($col_order, $col_visib) = $this->_getColumnParams($analyzed_sql); + + // Correction University of Virginia 19991216 in the while below + // Previous code assumed that all tables have keys, specifically that + // the phpMyAdmin GUI should support row delete/edit only for such + // tables. + // Although always using keys is arguably the prescribed way of + // defining a relational table, it is not required. This will in + // particular be violated by the novice. + // We want to encourage phpMyAdmin usage by such novices. So the code + // below has been changed to conditionally work as before when the + // table being displayed has one or more keys; but to display + // delete/edit options correctly for tables without keys. + + $odd_row = true; + $directionCondition + = ($_SESSION['tmp_user_values']['disp_direction'] + == self::DISP_DIR_HORIZONTAL) + || ($_SESSION['tmp_user_values']['disp_direction'] + == self::DISP_DIR_HORIZONTAL_FLIPPED); + + while ($row = PMA_DBI_fetch_row($dt_result)) { + + // "vertical display" mode stuff + $table_body_html .= $this->_getVerticalDisplaySupportSegments( + $vertical_display, $row_no, $directionCondition + ); + + $alternating_color_class = ($odd_row ? 'odd' : 'even'); + $odd_row = ! $odd_row; + + if ($directionCondition) { + // pointer code part + $table_body_html .= '<tr class="' . $alternating_color_class . '">'; + } + + // 1. Prepares the row + // 1.1 Results from a "SELECT" statement -> builds the + // WHERE clause to use in links (a unique key if possible) + /** + * @todo $where_clause could be empty, for example a table + * with only one field and it's a BLOB; in this case, + * avoid to display the delete and edit links + */ + list($where_clause, $clause_is_unique, $condition_array) + = PMA_Util::getUniqueCondition( + $dt_result, + $this->__get('fields_cnt'), + $this->__get('fields_meta'), + $row + ); + $where_clause_html = urlencode($where_clause); + + // In print view these variable needs toinitialized + $del_url = $del_query = $del_str = $edit_anchor_class + = $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null; + + // 1.2 Defines the URLs for the modify/delete link(s) + + if (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + || ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE) + ) { + // We need to copy the value + // or else the == 'both' check will always return true + + if ($GLOBALS['cfg']['ActionLinksMode'] === self::POSITION_BOTH) { + $iconic_spacer = '<div class="nowrap">'; + } else { + $iconic_spacer = ''; + } + + // 1.2.1 Modify link(s) - update row case + if ($is_display['edit_lnk'] == self::UPDATE_ROW) { + + list($edit_url, $copy_url, $edit_str, $copy_str, + $edit_anchor_class) + = $this->_getModifiedLinks( + $where_clause, + $clause_is_unique, $url_sql_query + ); + + } // end if (1.2.1) + + // 1.2.2 Delete/Kill link(s) + if (($is_display['del_lnk'] == self::DELETE_ROW) + || ($is_display['del_lnk'] == self::KILL_PROCESS) + ) { + + list($del_query, $del_url, $del_str, $js_conf) + = $this->_getDeleteAndKillLinks( + $where_clause, $clause_is_unique, + $url_sql_query, $is_display['del_lnk'], + $row + ); + + } // end if (1.2.2) + + // 1.3 Displays the links at left if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && $directionCondition + ) { + + $table_body_html .= $this->_getPlacedLinks( + self::POSITION_LEFT, $del_url, $is_display, $row_no, + $where_clause, $where_clause_html, $condition_array, + $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, + $edit_str, $copy_str, $del_str, $js_conf + ); + + } elseif (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) + && $directionCondition + ) { + + $table_body_html .= $this->_getPlacedLinks( + self::POSITION_NONE, $del_url, $is_display, $row_no, + $where_clause, $where_clause_html, $condition_array, + $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, + $edit_str, $copy_str, $del_str, $js_conf + ); + + } // end if (1.3) + } // end if (1) + + // 2. Displays the rows' values + $table_body_html .= $this->_getRowValues( + $dt_result, $row, $row_no, $col_order, $map, + $grid_edit_class, $col_visib, $where_clause, + $url_sql_query, $analyzed_sql, $directionCondition + ); + + // 3. Displays the modify/delete links on the right if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && $directionCondition + ) { + + $table_body_html .= $this->_getPlacedLinks( + self::POSITION_RIGHT, $del_url, $is_display, $row_no, + $where_clause, $where_clause_html, $condition_array, + $del_query, 'r', $edit_url, $copy_url, $edit_anchor_class, + $edit_str, $copy_str, $del_str, $js_conf + ); + + } // end if (3) + + if ($directionCondition) { + $table_body_html .= '</tr>'; + } // end if + + // 4. Gather links of del_urls and edit_urls in an array for later + // output + $this->_gatherLinksForLaterOutputs( + $row_no, $is_display, $where_clause, $where_clause_html, $js_conf, + $del_url, $del_query, $del_str, $edit_anchor_class, $edit_url, + $edit_str, $copy_url, $copy_str, $alternating_color_class, + $condition_array + ); + + $table_body_html .= $directionCondition ? "\n" : ''; + $row_no++; + + } // end while + + return $table_body_html; + + } // end of the '_getTableBody()' function + + + /** + * Get the values for one data row + * + * @param integer &$dt_result the link id associated to the query + * which results have to be displayed + * @param array $row current row data + * @param integer $row_no the index of current row + * @param array $col_order the column order + * false when a property not found + * @param array $map the list of relations + * @param string $grid_edit_class the class for all editable columns + * @param boolean $col_visib column is visible(false) + * array column isn't visible(string array) + * @param string $where_clause where clause + * @param string $url_sql_query the analyzed sql query + * @param array $analyzed_sql the analyzed query + * @param boolean $directionCondition the directional condition + * + * @return string $row_values_html html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getRowValues( + &$dt_result, $row, $row_no, $col_order, $map, + $grid_edit_class, $col_visib, $where_clause, + $url_sql_query, $analyzed_sql, $directionCondition + ) { + + $row_values_html = ''; + + // Following variable are needed for use in isset/empty or + // use with array indexes/safe use in foreach + $sql_query = $this->__get('sql_query'); + $fields_meta = $this->__get('fields_meta'); + $highlight_columns = $this->__get('highlight_columns'); + $mime_map = $this->__get('mime_map'); + + $row_info = $this->_getRowInfoForSpecialLinks($row, $col_order); + + for ($currentColumn = 0; + $currentColumn < $this->__get('fields_cnt'); + ++$currentColumn) { + + // assign $i with appropriate column order + $i = $col_order ? $col_order[$currentColumn] : $currentColumn; + + $meta = $fields_meta[$i]; + $not_null_class = $meta->not_null ? 'not_null' : ''; + $relation_class = isset($map[$meta->name]) ? 'relation' : ''; + $hide_class = ($col_visib && ! $col_visib[$currentColumn] + // hide per <td> only if the display dir is not vertical + && ($_SESSION['tmp_user_values']['disp_direction'] + != self::DISP_DIR_VERTICAL)) + ? 'hide' + : ''; + + // handle datetime-related class, for grid editing + $field_type_class + = $this->_getClassForDateTimeRelatedFields($meta->type); + + $is_field_truncated = false; + // combine all the classes applicable to this column's value + $class = $this->_getClassesForColumn( + $grid_edit_class, $not_null_class, $relation_class, + $hide_class, $field_type_class, $row_no + ); + + // See if this column should get highlight because it's used in the + // where-query. + $condition_field = (isset($highlight_columns) + && (isset($highlight_columns[$meta->name]) + || isset($highlight_columns[PMA_Util::backquote($meta->name)]))) + ? true + : false; + + // Wrap MIME-transformations. [MIME] + $default_function = '_mimeDefaultFunction'; // default_function + $transformation_plugin = $default_function; + $transform_options = array(); + + if ($GLOBALS['cfgRelation']['mimework'] + && $GLOBALS['cfg']['BrowseMIME'] + ) { + + if (isset($mime_map[$meta->name]['mimetype']) + && isset($mime_map[$meta->name]['transformation']) + && !empty($mime_map[$meta->name]['transformation']) + ) { + + $file = $mime_map[$meta->name]['transformation']; + $include_file = 'libraries/plugins/transformations/' . $file; + + if (file_exists($include_file)) { + + include_once $include_file; + $class_name = str_replace('.class.php', '', $file); + // todo add $plugin_manager + $plugin_manager = null; + $transformation_plugin = new $class_name( + $plugin_manager + ); + + $transform_options = PMA_transformation_getOptions( + isset($mime_map[$meta->name] + ['transformation_options'] + ) + ? $mime_map[$meta->name] + ['transformation_options'] + : '' + ); + + $meta->mimetype = str_replace( + '_', '/', + $mime_map[$meta->name]['mimetype'] + ); + + } // end if file_exists + } // end if transformation is set + } // end if mime/transformation works. + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'where_clause' => $where_clause, + 'transform_key' => $meta->name, + ); + + if (! empty($sql_query)) { + $_url_params['sql_query'] = $url_sql_query; + } + + $transform_options['wrapper_link'] + = PMA_generate_common_url($_url_params); + + $vertical_display = $this->__get('vertical_display'); + + // Check whether the field needs to display with syntax highlighting + + if ($this->_isNeedToSyntaxHighlight($meta->name) + && (trim($row[$i]) != '') + ) { + + $parsed_sql = PMA_SQP_parse($row[$i]); + $row[$i] = PMA_Util::formatSql( + $parsed_sql, $row[$i] + ); + include_once $this->transformation_info[strtolower($this->__get('db'))][strtolower($this->__get('table'))][strtolower($meta->name)][0]; + $transformation_plugin = new $this->transformation_info[strtolower($this->__get('db'))][strtolower($this->__get('table'))][strtolower($meta->name)][1](null); + + $transform_options = PMA_transformation_getOptions( + isset($mime_map[$meta->name]['transformation_options']) + ? $mime_map[$meta->name]['transformation_options'] + : '' + ); + + $meta->mimetype = str_replace( + '_', '/', + $this->transformation_info[strtolower($this->__get('db'))][strtolower($this->__get('table'))][strtolower($meta->name)][2] + ); + + } + + // Check for the predefined fields need to show as link in schemas + include_once 'libraries/special_schema_links.lib.php'; + + if (isset($GLOBALS['special_schema_links']) + && ($this->_isFieldNeedToLink(strtolower($meta->name))) + ) { + + $linking_url = $this->_getSpecialLinkUrl( + $row[$i], $row_info, strtolower($meta->name) + ); + include_once "libraries/plugins/transformations/Text_Plain_Link.class.php"; + $transformation_plugin = new Text_Plain_Link(null); + + $transform_options = array( + 0 => $linking_url, + 2 => true + ); + + $meta->mimetype = str_replace( + '_', '/', + 'Text/Plain' + ); + + } + + if ($meta->numeric == 1) { + // n u m e r i c + + $vertical_display['data'][$row_no][$i] + = $this->_getDataCellForNumericColumns( + $row[$i], $class, $condition_field, $meta, $map, + $is_field_truncated, $analyzed_sql, + $transformation_plugin, $default_function, + $transform_options + ); + + } elseif (stristr($meta->type, self::BLOB_FIELD)) { + // b l o b + + // PMA_mysql_fetch_fields returns BLOB in place of + // TEXT fields type so we have to ensure it's really a BLOB + $field_flags = PMA_DBI_field_flags($dt_result, $i); + + $vertical_display['data'][$row_no][$i] + = $this->_getDataCellForBlobColumns( + $row[$i], $class, $meta, $_url_params, $field_flags, + $transformation_plugin, $default_function, + $transform_options, $condition_field, $is_field_truncated + ); + + } elseif ($meta->type == self::GEOMETRY_FIELD) { + // g e o m e t r y + + // Remove 'grid_edit' from $class as we do not allow to + // inline-edit geometry data. + $class = str_replace('grid_edit', '', $class); + + $vertical_display['data'][$row_no][$i] + = $this->_getDataCellForGeometryColumns( + $row[$i], $class, $meta, $map, $_url_params, + $condition_field, $transformation_plugin, + $default_function, $transform_options, + $is_field_truncated, $analyzed_sql + ); + + } else { + // n o t n u m e r i c a n d n o t B L O B + + $vertical_display['data'][$row_no][$i] + = $this->_getDataCellForNonNumericAndNonBlobColumns( + $row[$i], $class, $meta, $map, $_url_params, + $condition_field, $transformation_plugin, + $default_function, $transform_options, + $is_field_truncated, $analyzed_sql, $dt_result, $i + ); + + } + + // output stored cell + if ($directionCondition) { + $row_values_html + .= $vertical_display['data'][$row_no][$i]; + } + + if (isset($vertical_display['rowdata'][$i][$row_no])) { + $vertical_display['rowdata'][$i][$row_no] + .= $vertical_display['data'][$row_no][$i]; + } else { + $vertical_display['rowdata'][$i][$row_no] + = $vertical_display['data'][$row_no][$i]; + } + + $this->__set('vertical_display', $vertical_display); + + } // end for + + return $row_values_html; + + } // end of the '_getRowValues()' function + + + /** + * Gather delete/edit url links for further outputs + * + * @param integer $row_no the index of current row + * @param array $is_display which elements to display + * @param string $where_clause where clause + * @param string $where_clause_html the html encoded where clause + * @param string $js_conf text for the JS confirmation + * @param string $del_url the url for delete row + * @param string $del_query the query for delete row + * @param string $del_str the label for delete row + * @param string $edit_anchor_class the class for html element for edit + * @param string $edit_url the url for edit row + * @param string $edit_str the label for edit row + * @param string $copy_url the url for copy row + * @param string $copy_str the label for copy row + * @param string $alternating_color_class class for display two colors in rows + * @param array $condition_array array of keys + * (primary,unique,condition) + * + * @return void + * + * @access private + * + * @see _getTableBody() + */ + private function _gatherLinksForLaterOutputs( + $row_no, $is_display, $where_clause, $where_clause_html, $js_conf, + $del_url, $del_query, $del_str, $edit_anchor_class, $edit_url, $edit_str, + $copy_url, $copy_str, $alternating_color_class, $condition_array + ) { + + $vertical_display = $this->__get('vertical_display'); + + if (! isset($vertical_display['edit'][$row_no])) { + $vertical_display['edit'][$row_no] = ''; + $vertical_display['copy'][$row_no] = ''; + $vertical_display['delete'][$row_no] = ''; + $vertical_display['row_delete'][$row_no] = ''; + } + + $vertical_class = ' row_' . $row_no; + if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) { + $vertical_class .= ' vpointer'; + } + + if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) { + $vertical_class .= ' vmarker'; + } + + if (!empty($del_url) + && ($is_display['del_lnk'] != self::KILL_PROCESS) + ) { + + $vertical_display['row_delete'][$row_no] + .= $this->_getCheckboxForMultiRowSubmissions( + $del_url, $is_display, $row_no, $where_clause_html, + $condition_array, $del_query, '[%_PMA_CHECKBOX_DIR_%]', + $alternating_color_class . $vertical_class + ); + + } else { + unset($vertical_display['row_delete'][$row_no]); + } + + if (isset($edit_url)) { + + $vertical_display['edit'][$row_no] .= $this->_getEditLink( + $edit_url, + $alternating_color_class . ' ' . $edit_anchor_class + . $vertical_class, $edit_str, + $where_clause, + $where_clause_html + ); + + } else { + unset($vertical_display['edit'][$row_no]); + } + + if (isset($copy_url)) { + + $vertical_display['copy'][$row_no] .= $this->_getCopyLink( + $copy_url, $copy_str, $where_clause, $where_clause_html, + $alternating_color_class . $vertical_class + ); + + } else { + unset($vertical_display['copy'][$row_no]); + } + + if (isset($del_url)) { + + if (! isset($js_conf)) { + $js_conf = ''; + } + + $vertical_display['delete'][$row_no] + .= $this->_getDeleteLink( + $del_url, $del_str, $js_conf, + $alternating_color_class . $vertical_class + ); + + } else { + unset($vertical_display['delete'][$row_no]); + } + + $this->__set('vertical_display', $vertical_display); + + } // end of the '_gatherLinksForLaterOutputs()' function + + + /** + * Check whether any field is marked as need to syntax highlight + * + * @param string $field field to check + * + * @return boolean + */ + private function _isNeedToSyntaxHighlight($field) + { + if (! empty($this->transformation_info[strtolower($this->__get('db'))][strtolower($this->__get('table'))][strtolower($field)])) { + return true; + } + return false; + } + + /** + * Check whether the field needs to be link + * + * @param string $field field to check + * + * @return boolean + */ + private function _isFieldNeedToLink($field) + { + if (! empty($GLOBALS['special_schema_links'][strtolower($this->__get('db'))][strtolower($this->__get('table'))][$field])) { + return true; + } + return false; + } + + + /** + * Get link for display special schema links + * + * @param string $column_value column value + * @param array $row_info information about row + * @param string $field_name column name + * + * @return string generated link + */ + private function _getSpecialLinkUrl($column_value, $row_info, $field_name) + { + + $linking_url_params = array(); + $link_relations = $GLOBALS['special_schema_links'] + [strtolower($this->__get('db'))] + [strtolower($this->__get('table'))] + [$field_name]; + + if (! is_array($link_relations['link_param'])) { + $linking_url_params[$link_relations['link_param']] = $column_value; + } else { + // Consider only the case of creating link for column field + // sql query need to be pass as url param + $sql = 'SELECT `'.$column_value.'` FROM `' + . $row_info[$link_relations['link_param'][1]] .'`.`' + . $row_info[$link_relations['link_param'][2]] .'`'; + $linking_url_params[$link_relations['link_param'][0]] = $sql; + } + + + if (! empty($link_relations['link_dependancy_params'])) { + + foreach ($link_relations['link_dependancy_params'] as $new_param) { + + // If param_info is an array, set the key and value + // from that array + if (is_array($new_param['param_info'])) { + $linking_url_params[$new_param['param_info'][0]] + = $new_param['param_info'][1]; + } else { + + $linking_url_params[$new_param['param_info']] + = $row_info[strtolower($new_param['column_name'])]; + + // Special case 1 - when executing routines, according + // to the type of the routine, url param changes + if (!empty($row_info['routine_type'])) { + if (strtolower($row_info['routine_type']) == self::ROUTINE_PROCEDURE + || strtolower($row_info['routine_type']) == self::ROUTINE_FUNCTION + ) { + $linking_url_params['edit_item'] = 1; + } + } + } + + } + + } + + return $link_relations['default_page'] + . PMA_generate_common_url($linking_url_params); + + } + + + /** + * Prepare row information for display special links + * + * @param array $row current row data + * @param array $col_order the column order + * + * @return array $row_info associative array with column nama -> value + */ + private function _getRowInfoForSpecialLinks($row, $col_order) + { + + $row_info = array(); + $fields_meta = $this->__get('fields_meta'); + + for ($n = 0; $n < $this->__get('fields_cnt'); ++$n) { + $m = $col_order ? $col_order[$n] : $n; + $row_info[strtolower($fields_meta[$m]->name)] = $row[$m]; + } + + return $row_info; + + } + + + /** + * Get url sql query without conditions to shorten URLs + * + * @param array $analyzed_sql analyzed query + * + * @return string $url_sql analyzed sql query + * + * @access private + * + * @see _getTableBody() + */ + private function _getUrlSqlQuery($analyzed_sql) + { + + if (isset($analyzed_sql) + && isset($analyzed_sql[0]) + && isset($analyzed_sql[0]['querytype']) + && ($analyzed_sql[0]['querytype'] == self::QUERY_TYPE_SELECT) + && (strlen($this->__get('sql_query')) > 200) + ) { + + $url_sql_query = 'SELECT '; + if (isset($analyzed_sql[0]['queryflags']['distinct'])) { + $url_sql_query .= ' DISTINCT '; + } + + $url_sql_query .= $analyzed_sql[0]['select_expr_clause']; + if (!empty($analyzed_sql[0]['from_clause'])) { + $url_sql_query .= ' FROM ' . $analyzed_sql[0]['from_clause']; + } + + return $url_sql_query; + } + + return $this->__get('sql_query'); + + } // end of the '_getUrlSqlQuery()' function + + + /** + * Get column order and column visibility + * + * @param array $analyzed_sql the analyzed query + * + * @return array 2 element array - $col_order, $col_visib + * + * @access private + * + * @see _getTableBody() + */ + private function _getColumnParams($analyzed_sql) + { + if ($this->_isSelect($analyzed_sql)) { + $pmatable = new PMA_Table($this->__get('table'), $this->__get('db')); + $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER); + $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB); + } else { + $col_order = false; + $col_visib = false; + } + + return array($col_order, $col_visib); + } // end of the '_getColumnParams()' function + + + /** + * Prepare vertical display mode necessay HTML stuff + * + * @param array $vertical_display informations used with vertical + * display mode + * @param integer $row_no the index of current row + * @param boolean $directionCondition the directional condition + * + * @return string $vertical_disp_html html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getVerticalDisplaySupportSegments( + $vertical_display, $row_no, $directionCondition + ) { + + $support_html = ''; + + if ((($row_no != 0) && ($_SESSION['tmp_user_values']['repeat_cells'] != 0)) + && !($row_no % $_SESSION['tmp_user_values']['repeat_cells']) + && $directionCondition + ) { + + $support_html .= '<tr>' . "\n"; + + if ($vertical_display['emptypre'] > 0) { + + $support_html .= ' <th colspan="' + . $vertical_display['emptypre'] . '">' + . "\n".' </th>' . "\n"; + + } else if ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) { + $support_html .= ' <th></th>' . "\n"; + } + + foreach ($vertical_display['desc'] as $val) { + $support_html .= $val; + } + + if ($vertical_display['emptyafter'] > 0) { + $support_html + .= ' <th colspan="' . $vertical_display['emptyafter'] + . '">' + . "\n" . ' </th>' . "\n"; + } + $support_html .= '</tr>' . "\n"; + } // end if + + return $support_html; + + } // end of the '_getVerticalDisplaySupportSegments()' function + + + /** + * Get modified links + * + * @param string $where_clause the where clause of the sql + * @param boolean $clause_is_unique the unique condition of clause + * @param string $url_sql_query the analyzed sql query + * + * @return array 5 element array - $edit_url, $copy_url, + * $edit_str, $copy_str, $edit_anchor_class + * + * @access private + * + * @see _getTableBody() + */ + private function _getModifiedLinks( + $where_clause, $clause_is_unique, $url_sql_query + ) { + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'where_clause' => $where_clause, + 'clause_is_unique' => $clause_is_unique, + 'sql_query' => $url_sql_query, + 'goto' => 'sql.php', + ); + + $edit_url = 'tbl_change.php' + . PMA_generate_common_url( + $_url_params + array('default_action' => 'update') + ); + + $copy_url = 'tbl_change.php' + . PMA_generate_common_url( + $_url_params + array('default_action' => 'insert') + ); + + $edit_str = PMA_Util::getIcon( + 'b_edit.png', __('Edit') + ); + $copy_str = PMA_Util::getIcon( + 'b_insrow.png', __('Copy') + ); + + // Class definitions required for grid editing jQuery scripts + $edit_anchor_class = "edit_row_anchor"; + if ( $clause_is_unique == 0) { + $edit_anchor_class .= ' nonunique'; + } + + return array($edit_url, $copy_url, $edit_str, $copy_str, $edit_anchor_class); + + } // end of the '_getModifiedLinks()' function + + + /** + * Get delete and kill links + * + * @param string $where_clause the where clause of the sql + * @param boolean $clause_is_unique the unique condition of clause + * @param string $url_sql_query the analyzed sql query + * @param string $del_lnk the delete link of current row + * @param array $row the current row + * + * @return array 4 element array - $del_query, + * $del_url, $del_str, $js_conf + * + * @access private + * + * @see _getTableBody() + */ + private function _getDeleteAndKillLinks( + $where_clause, $clause_is_unique, $url_sql_query, $del_lnk, $row + ) { + + $goto = $this->__get('goto'); + + if ($del_lnk == self::DELETE_ROW) { // delete row case + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $url_sql_query, + 'message_to_show' => __('The row has been deleted'), + 'goto' => (empty($goto) ? 'tbl_sql.php' : $goto), + ); + + $lnk_goto = 'sql.php' . PMA_generate_common_url($_url_params, 'text'); + + $del_query = 'DELETE FROM ' + . PMA_Util::backquote($this->__get('db')) . '.' + . PMA_Util::backquote($this->__get('table')) + . ' WHERE ' . $where_clause . + ($clause_is_unique ? '' : ' LIMIT 1'); + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $del_query, + 'message_to_show' => __('The row has been deleted'), + 'goto' => $lnk_goto, + ); + $del_url = 'sql.php' . PMA_generate_common_url($_url_params); + + $js_conf = 'DELETE FROM ' . PMA_jsFormat($this->__get('db')) . '.' + . PMA_jsFormat($this->__get('table')) + . ' WHERE ' . PMA_jsFormat($where_clause, false) + . ($clause_is_unique ? '' : ' LIMIT 1'); + + $del_str = PMA_Util::getIcon( + 'b_drop.png', __('Delete') + ); + + } elseif ($del_lnk == self::KILL_PROCESS) { // kill process case + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $url_sql_query, + 'goto' => 'index.php', + ); + + $lnk_goto = 'sql.php' + . PMA_generate_common_url( + $_url_params, 'text' + ); + + $_url_params = array( + 'db' => 'mysql', + 'sql_query' => 'KILL ' . $row[0], + 'goto' => $lnk_goto, + ); + + $del_url = 'sql.php' . PMA_generate_common_url($_url_params); + $del_query = 'KILL ' . $row[0]; + $js_conf = 'KILL ' . $row[0]; + $del_str = PMA_Util::getIcon( + 'b_drop.png', __('Kill') + ); + } + + return array($del_query, $del_url, $del_str, $js_conf); + + } // end of the '_getDeleteAndKillLinks()' function + + + /** + * Prepare placed links + * + * @param string $dir the direction of links should place + * @param string $del_url the url for delete row + * @param array $is_display which elements to display + * @param integer $row_no the index of current row + * @param string $where_clause the where clause of the sql + * @param string $where_clause_html the html encoded where clause + * @param array $condition_array array of keys (primary, unique, condition) + * @param string $del_query the query for delete row + * @param string $dir_letter the letter denoted the direction + * @param string $edit_url the url for edit row + * @param string $copy_url the url for copy row + * @param string $edit_anchor_class the class for html element for edit + * @param string $edit_str the label for edit row + * @param string $copy_str the label for copy row + * @param string $del_str the label for delete row + * @param string $js_conf text for the JS confirmation + * + * @return string html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getPlacedLinks( + $dir, $del_url, $is_display, $row_no, $where_clause, $where_clause_html, + $condition_array, $del_query, $dir_letter, $edit_url, $copy_url, + $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf + ) { + + if (! isset($js_conf)) { + $js_conf = ''; + } + + return $this->_getCheckboxAndLinks( + $dir, $del_url, $is_display, + $row_no, $where_clause, $where_clause_html, $condition_array, + $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, + $edit_str, $copy_str, $del_str, $js_conf + ); + + } // end of the '_getPlacedLinks()' function + + + /** + * Get the combined classes for a column + * + * @param string $grid_edit_class the class for all editable columns + * @param string $not_null_class the class for not null columns + * @param string $relation_class the class for relations in a column + * @param string $hide_class the class for visibility of a column + * @param string $field_type_class the class related to type of the field + * @param integer $row_no the row index + * + * @return string $class the combined classes + * + * @access private + * + * @see _getTableBody() + */ + private function _getClassesForColumn( + $grid_edit_class, $not_null_class, $relation_class, + $hide_class, $field_type_class, $row_no + ) { + + $printview = $this->__get('printview'); + + $class = 'data ' . $grid_edit_class . ' ' . $not_null_class . ' ' + . $relation_class . ' ' . $hide_class . ' ' . $field_type_class; + + if (($_SESSION['tmp_user_values']['disp_direction'] == self::DISP_DIR_VERTICAL) + && (! isset($printview) || ($printview != '1')) + ) { + // the row number corresponds to a data row, not HTML table row + $class .= ' row_' . $row_no; + if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) { + $class .= ' vpointer'; + } + + if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) { + $class .= ' vmarker'; + } + } + + return $class; + + } // end of the '_getClassesForColumn()' function + + + /** + * Get class for datetime related fields + * + * @param string $type the type of the column field + * + * @return string $field_type_class the class for the column + * + * @access private + * + * @see _getTableBody() + */ + private function _getClassForDateTimeRelatedFields($type) + { + if ((substr($type, 0, 9) == self::TIMESTAMP_FIELD) + || ($type == self::DATETIME_FIELD) + ) { + $field_type_class = 'datetimefield'; + } else if ($type == self::DATE_FIELD) { + $field_type_class = 'datefield'; + } else { + $field_type_class = ''; + } + return $field_type_class; + } // end of the '_getClassForDateTimeRelatedFields()' function + + + /** + * Prepare data cell for numeric type fields + * + * @param string $column the relavent column in data row + * @param string $class the html class for column + * @param boolean $condition_field the column should highlighted + * or not + * @param object $meta the meta-information about this + * field + * @param array $map the list of relations + * @param boolean $is_field_truncated the condition for blob data + * replacements + * @param array $analyzed_sql the analyzed query + * @param string $transformation_plugin the name of transformation plugin + * @param string $default_function the default transformation function + * @param string $transform_options the transformation parameters + * + * @return string $cell the prepared cell, html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getDataCellForNumericColumns( + $column, $class, $condition_field, $meta, $map, $is_field_truncated, + $analyzed_sql, $transformation_plugin, $default_function, + $transform_options + ) { + + if (! isset($column) || is_null($column)) { + + $cell = $this->_buildNullDisplay( + 'right '.$class, $condition_field, $meta, '' + ); + + } elseif ($column != '') { + + $nowrap = ' nowrap'; + $where_comparison = ' = ' . $column; + + $cell = $this->_getRowData( + 'right '.$class, $condition_field, + $analyzed_sql, $meta, $map, $column, + $transformation_plugin, $default_function, $nowrap, + $where_comparison, $transform_options, + $is_field_truncated + ); + } else { + + $cell = $this->_buildEmptyDisplay( + 'right '.$class, $condition_field, $meta, '' + ); + } + + return $cell; + + } // end of the '_getDataCellForNumericColumns()' function + + + /** + * Get data cell for blob type fields + * + * @param string $column the relavent column in data row + * @param string $class the html class for column + * @param object $meta the meta-information about this + * field + * @param array $_url_params the parameters for generate url + * @param string $field_flags field flags for column(blob, + * primary etc) + * @param string $transformation_plugin the name of transformation function + * @param string $default_function the default transformation function + * @param string $transform_options the transformation parameters + * @param boolean $condition_field the column should highlighted + * or not + * @param boolean $is_field_truncated the condition for blob data + * replacements + * + * @return string $cell the prepared cell, html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getDataCellForBlobColumns( + $column, $class, $meta, $_url_params, $field_flags, $transformation_plugin, + $default_function, $transform_options, $condition_field, $is_field_truncated + ) { + + if (stristr($field_flags, self::BINARY_FIELD)) { + + // remove 'grid_edit' from $class as we can't edit binary data. + $class = str_replace('grid_edit', '', $class); + + if (! isset($column) || is_null($column)) { + + $cell = $this->_buildNullDisplay($class, $condition_field, $meta); + + } else { + + $blobtext = $this->_handleNonPrintableContents( + self::BLOB_FIELD, (isset($column) ? $column : ''), + $transformation_plugin, $transform_options, + $default_function, $meta, $_url_params + ); + + $cell = $this->_buildValueDisplay( + $class, $condition_field, $blobtext + ); + unset($blobtext); + } + } else { + // not binary: + + if (! isset($column) || is_null($column)) { + + $cell = $this->_buildNullDisplay($class, $condition_field, $meta); + + } elseif ($column != '') { + + // if a transform function for blob is set, none of these + // replacements will be made + if ((PMA_strlen($column) > $GLOBALS['cfg']['LimitChars']) + && ($_SESSION['tmp_user_values']['display_text'] == self::DISPLAY_PARTIAL_TEXT) + && ! $this->_isNeedToSyntaxHighlight(strtolower($meta->name)) + ) { + $column = PMA_substr($column, 0, $GLOBALS['cfg']['LimitChars']) + . '...'; + $is_field_truncated = true; + } + + // displays all space characters, 4 space + // characters for tabulations and <cr>/<lf> + $column = ($default_function != $transformation_plugin) + ? $transformation_plugin->applyTransformation( + $column, + $transform_options, + $meta + ) + : $this->$default_function($column, array(), $meta); + + if ($is_field_truncated) { + $class .= ' truncated'; + } + + $cell = $this->_buildValueDisplay($class, $condition_field, $column); + + } else { + $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta); + } + } + + return $cell; + + } // end of the '_getDataCellForBlobColumns()' function + + + /** + * Get data cell for geometry type fields + * + * @param string $column the relavent column in data row + * @param string $class the html class for column + * @param object $meta the meta-information about this field + * @param array $map the list of relations + * @param array $_url_params the parameters for generate url + * @param boolean $condition_field the column should highlighted or not + * @param string $transformation_plugin the name of transformation function + * @param string $default_function the default transformation function + * @param string $transform_options the transformation parameters + * @param boolean $is_field_truncated the condition for blob data replacements + * @param array $analyzed_sql the analyzed query + * + * @return string $cell the prepared data cell, html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getDataCellForGeometryColumns( + $column, $class, $meta, $map, $_url_params, $condition_field, + $transformation_plugin, $default_function, $transform_options, + $is_field_truncated, $analyzed_sql + ) { + + if (! isset($column) || is_null($column)) { + + $cell = $this->_buildNullDisplay($class, $condition_field, $meta); + + } elseif ($column != '') { + + // Display as [GEOMETRY - (size)] + if ($_SESSION['tmp_user_values']['geometry_display'] == self::GEOMETRY_DISP_GEOM) { + + $geometry_text = $this->_handleNonPrintableContents( + strtoupper(self::GEOMETRY_FIELD), + (isset($column) ? $column : ''), $transformation_plugin, + $transform_options, $default_function, $meta + ); + + $cell = $this->_buildValueDisplay( + $class, $condition_field, $geometry_text + ); + + } elseif ($_SESSION['tmp_user_values']['geometry_display'] + == self::GEOMETRY_DISP_WKT + ) { + // Prepare in Well Known Text(WKT) format. + + $where_comparison = ' = ' . $column; + + // Convert to WKT format + $wktval = PMA_Util::asWKT($column); + + if ((PMA_strlen($wktval) > $GLOBALS['cfg']['LimitChars']) + && ($_SESSION['tmp_user_values']['display_text'] == self::DISPLAY_PARTIAL_TEXT) + ) { + $wktval = PMA_substr($wktval, 0, $GLOBALS['cfg']['LimitChars']) + . '...'; + $is_field_truncated = true; + } + + $cell = $this->_getRowData( + $class, $condition_field, $analyzed_sql, $meta, $map, + $wktval, $transformation_plugin, $default_function, '', + $where_comparison, $transform_options, + $is_field_truncated + ); + + } else { + // Prepare in Well Known Binary (WKB) format. + + if ($_SESSION['tmp_user_values']['display_binary']) { + + $where_comparison = ' = ' . $column; + + $wkbval = $this->_displayBinaryAsPrintable($column, 'binary', 8); + + if ((PMA_strlen($wkbval) > $GLOBALS['cfg']['LimitChars']) + && ($_SESSION['tmp_user_values']['display_text'] == self::DISPLAY_PARTIAL_TEXT) + ) { + $wkbval + = PMA_substr($wkbval, 0, $GLOBALS['cfg']['LimitChars']) + . '...'; + $is_field_truncated = true; + } + + $cell = $this->_getRowData( + $class, $condition_field, + $analyzed_sql, $meta, $map, $wkbval, + $transformation_plugin, $default_function, '', + $where_comparison, $transform_options, + $is_field_truncated + ); + + } else { + $wkbval = $this->_handleNonPrintableContents( + self::BINARY_FIELD, $column, $transformation_plugin, + $transform_options, $default_function, $meta, + $_url_params + ); + + $cell = $this->_buildValueDisplay( + $class, $condition_field, $wkbval + ); + } + } + } else { + $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta); + } + + return $cell; + + } // end of the '_getDataCellForGeometryColumns()' function + + + /** + * Get data cell for non numeric and non blob type fields + * + * @param string $column the relavent column in data row + * @param string $class the html class for column + * @param object $meta the meta-information about the field + * @param array $map the list of relations + * @param array $_url_params the parameters for generate url + * @param boolean $condition_field the column should highlighted + * or not + * @param string $transformation_plugin the name of transformation function + * @param string $default_function the default transformation function + * @param string $transform_options the transformation parameters + * @param boolean $is_field_truncated the condition for blob data + * replacements + * @param array $analyzed_sql the analyzed query + * @param integer &$dt_result the link id associated to the query + * which results have to be displayed + * @param integer $col_index the column index + * + * @return string $cell the prepared data cell, html content + * + * @access private + * + * @see _getTableBody() + */ + private function _getDataCellForNonNumericAndNonBlobColumns( + $column, $class, $meta, $map, $_url_params, $condition_field, + $transformation_plugin, $default_function, $transform_options, + $is_field_truncated, $analyzed_sql, &$dt_result, $col_index + ) { + + $is_analyse = $this->__get('is_analyse'); + $field_flags = PMA_DBI_field_flags($dt_result, $col_index); + if (stristr($field_flags, self::BINARY_FIELD) + && ($GLOBALS['cfg']['ProtectBinary'] == 'all' + || $GLOBALS['cfg']['ProtectBinary'] == 'noblob') + ) { + $class = str_replace('grid_edit', '', $class); + } + + if (! isset($column) || is_null($column)) { + + $cell = $this->_buildNullDisplay($class, $condition_field, $meta); + + } elseif ($column != '') { + + // Cut all fields to $GLOBALS['cfg']['LimitChars'] + // (unless it's a link-type transformation) + if (PMA_strlen($column) > $GLOBALS['cfg']['LimitChars'] + && ($_SESSION['tmp_user_values']['display_text'] == self::DISPLAY_PARTIAL_TEXT) + && ! (gettype($transformation_plugin) == "object" + && strpos($transformation_plugin->getName(), 'Link') !== false) + ) { + $column = PMA_substr($column, 0, $GLOBALS['cfg']['LimitChars']) + . '...'; + $is_field_truncated = true; + } + + $formatted = false; + if (isset($meta->_type) && $meta->_type === MYSQLI_TYPE_BIT) { + + $column = PMA_Util::printableBitValue( + $column, $meta->length + ); + + // some results of PROCEDURE ANALYSE() are reported as + // being BINARY but they are quite readable, + // so don't treat them as BINARY + } elseif (stristr($field_flags, self::BINARY_FIELD) + && ($meta->type == self::STRING_FIELD) + && !(isset($is_analyse) && $is_analyse) + ) { + + if ($_SESSION['tmp_user_values']['display_binary']) { + + // user asked to see the real contents of BINARY + // fields + $column = $this->_displayBinaryAsPrintable($column, 'binary'); + + } else { + // we show the BINARY message and field's size + // (or maybe use a transformation) + $column = $this->_handleNonPrintableContents( + self::BINARY_FIELD, $column, $transformation_plugin, + $transform_options, $default_function, + $meta, $_url_params + ); + $formatted = true; + } + } + + if ($formatted) { + + $cell = $this->_buildValueDisplay( + $class, $condition_field, $column + ); + + } else { + + // transform functions may enable no-wrapping: + $function_nowrap = 'applyTransformationNoWrap'; + + $bool_nowrap = (($default_function != $transformation_plugin) + && function_exists($transformation_plugin->$function_nowrap())) + ? $transformation_plugin->$function_nowrap($transform_options) + : false; + + // do not wrap if date field type + $nowrap = (preg_match('@DATE|TIME@i', $meta->type) + || $bool_nowrap) ? ' nowrap' : ''; + + $where_comparison = ' = \'' + . PMA_Util::sqlAddSlashes($column) + . '\''; + + $cell = $this->_getRowData( + $class, $condition_field, + $analyzed_sql, $meta, $map, $column, + $transformation_plugin, $default_function, $nowrap, + $where_comparison, $transform_options, + $is_field_truncated + ); + } + + } else { + $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta); + } + + return $cell; + + } // end of the '_getDataCellForNonNumericAndNonBlobColumns()' function + + + /** + * Get the resulted table with the vertical direction mode. + * + * @param array $analyzed_sql the analyzed query + * @param array $is_display display mode + * + * @return string html content + * + * @access private + * + * @see _getTable() + */ + private function _getVerticalTable($analyzed_sql, $is_display) + { + + $vertical_table_html = ''; + $vertical_display = $this->__get('vertical_display'); + + // Prepares "multi row delete" link at top if required + if (($GLOBALS['cfg']['RowActionLinks'] != self::POSITION_RIGHT) + && is_array($vertical_display['row_delete']) + && ((count($vertical_display['row_delete']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + + $vertical_table_html .= '<tr>' . "\n"; + if ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) { + // if we are not showing the RowActionLinks, then we need to show + // the Multi-Row-Action checkboxes + $vertical_table_html .= '<th></th>' . "\n"; + } + + $vertical_table_html .= $vertical_display['textbtn'] + . $this->_getCheckBoxesForMultipleRowOperations('_left', $is_display) + . '</tr>' . "\n"; + } // end if + + // Prepares "edit" link at top if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['edit']) + && ((count($vertical_display['edit']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + $vertical_table_html .= $this->_getOperationLinksForVerticleTable( + 'edit' + ); + } // end if + + // Prepares "copy" link at top if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['copy']) + && ((count($vertical_display['copy']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + $vertical_table_html .= $this->_getOperationLinksForVerticleTable( + 'copy' + ); + } // end if + + // Prepares "delete" link at top if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['delete']) + && ((count($vertical_display['delete']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + $vertical_table_html .= $this->_getOperationLinksForVerticleTable( + 'delete' + ); + } // end if + + list($col_order, $col_visib) = $this->_getColumnParams($analyzed_sql); + + // Prepares data + foreach ($vertical_display['desc'] AS $j => $val) { + + // assign appropriate key with current column order + $key = $col_order ? $col_order[$j] : $j; + + $vertical_table_html .= '<tr' + . (($col_visib && !$col_visib[$j]) ? ' class="hide"' : '') + . '>' . "\n" + . $val; + + $cell_displayed = 0; + foreach ($vertical_display['rowdata'][$key] as $subval) { + + if (($cell_displayed != 0) + && ($_SESSION['tmp_user_values']['repeat_cells'] != 0) + && ! ($cell_displayed % $_SESSION['tmp_user_values']['repeat_cells']) + ) { + $vertical_table_html .= $val; + } + + $vertical_table_html .= $subval; + $cell_displayed++; + + } // end while + + $vertical_table_html .= '</tr>' . "\n"; + } // end while + + // Prepares "multi row delete" link at bottom if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['row_delete']) + && ((count($vertical_display['row_delete']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + + $vertical_table_html .= '<tr>' . "\n" + . $vertical_display['textbtn'] + . $this->_getCheckBoxesForMultipleRowOperations('_right', $is_display) + . '</tr>' . "\n"; + } // end if + + // Prepares "edit" link at bottom if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['edit']) + && ((count($vertical_display['edit']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + $vertical_table_html .= $this->_getOperationLinksForVerticleTable( + 'edit' + ); + } // end if + + // Prepares "copy" link at bottom if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['copy']) + && ((count($vertical_display['copy']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + $vertical_table_html .= $this->_getOperationLinksForVerticleTable( + 'copy' + ); + } // end if + + // Prepares "delete" link at bottom if required + if ((($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) + || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH)) + && is_array($vertical_display['delete']) + && ((count($vertical_display['delete']) > 0) + || !empty($vertical_display['textbtn'])) + ) { + $vertical_table_html .= $this->_getOperationLinksForVerticleTable( + 'delete' + ); + } + + return $vertical_table_html; + + } // end of the '_getVerticalTable' function + + + /** + * Prepare edit, copy and delete links for verticle table + * + * @param string $operation edit/copy/delete + * + * @return string $links_html html content + * + * @access private + * + * @see _getVerticalTable() + */ + private function _getOperationLinksForVerticleTable($operation) + { + + $link_html = '<tr>' . "\n"; + $vertical_display = $this->__get('vertical_display'); + + if (! is_array($vertical_display['row_delete'])) { + + if (($operation == 'edit') || ($operation == 'copy')) { + $link_html .= $vertical_display['textbtn']; + + } elseif ($operation == 'delete') { + + if (! is_array($vertical_display['edit'])) { + $link_html .= $vertical_display['textbtn']; + } + } + } + + foreach ($vertical_display[$operation] as $val) { + $link_html .= $val; + } // end while + + $link_html .= '</tr>' . "\n"; + + return $link_html; + + } // end of the '_getOperationLinksForVerticleTable' function + + + /** + * Get checkboxes for multiple row data operations + * + * @param string $dir _left / _right + * @param array $is_display display mode + * + * @return $checkBoxes_html html content + * + * @access private + * + * @see _getVerticalTable() + */ + private function _getCheckBoxesForMultipleRowOperations($dir, $is_display) + { + + $checkBoxes_html = ''; + $cell_displayed = 0; + $vertical_display = $this->__get('vertical_display'); + + foreach ($vertical_display['row_delete'] as $val) { + + if (($cell_displayed != 0) + && ($_SESSION['tmp_user_values']['repeat_cells'] != 0) + && !($cell_displayed % $_SESSION['tmp_user_values']['repeat_cells']) + ) { + + $checkBoxes_html .= '<th' + . (($is_display['edit_lnk'] != self::NO_EDIT_OR_DELETE) + && ($is_display['del_lnk'] != self::NO_EDIT_OR_DELETE)) + ? ' rowspan="4"' + : '' + . '></th>' . "\n"; + + } + + $checkBoxes_html .= str_replace('[%_PMA_CHECKBOX_DIR_%]', $dir, $val); + $cell_displayed++; + } // end while + + return $checkBoxes_html; + + } // end of the '_getCheckBoxesForMultipleRowOperations' function + + + /** + * Checks the posted options for viewing query resutls + * and sets appropriate values in the session. + * + * @todo make maximum remembered queries configurable + * @todo move/split into SQL class!? + * @todo currently this is called twice unnecessary + * @todo ignore LIMIT and ORDER in query!? + * + * @return void + * + * @access public + * + * @see sql.php file + */ + public function setConfigParamsForDisplayTable() + { + + $sql_md5 = md5($this->__get('sql_query')); + + $_SESSION['tmp_user_values']['query'][$sql_md5]['sql'] + = $this->__get('sql_query'); + + $valid_disp_dir = PMA_isValid( + $_REQUEST['disp_direction'], + array(self::DISP_DIR_HORIZONTAL, self::DISP_DIR_VERTICAL, + self::DISP_DIR_HORIZONTAL_FLIPPED + ) + ); + + if ($valid_disp_dir) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['disp_direction'] + = $_REQUEST['disp_direction']; + unset($_REQUEST['disp_direction']); + } elseif ( + empty($_SESSION['tmp_user_values']['query'][$sql_md5]['disp_direction']) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['disp_direction'] + = $GLOBALS['cfg']['DefaultDisplay']; + } + + if (PMA_isValid($_REQUEST['repeat_cells'], 'numeric')) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['repeat_cells'] + = $_REQUEST['repeat_cells']; + unset($_REQUEST['repeat_cells']); + } elseif ( + empty($_SESSION['tmp_user_values']['query'][$sql_md5]['repeat_cells']) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['repeat_cells'] + = $GLOBALS['cfg']['RepeatCells']; + } + + // as this is a form value, the type is always string so we cannot + // use PMA_isValid($_REQUEST['session_max_rows'], 'integer') + if ((PMA_isValid($_REQUEST['session_max_rows'], 'numeric') + && ((int) $_REQUEST['session_max_rows'] == $_REQUEST['session_max_rows'])) + || ($_REQUEST['session_max_rows'] == self::ALL_ROWS) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['max_rows'] + = $_REQUEST['session_max_rows']; + unset($_REQUEST['session_max_rows']); + } elseif (empty($_SESSION['tmp_user_values']['query'][$sql_md5]['max_rows'])) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['max_rows'] + = $GLOBALS['cfg']['MaxRows']; + } + + if (PMA_isValid($_REQUEST['pos'], 'numeric')) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['pos'] + = $_REQUEST['pos']; + unset($_REQUEST['pos']); + } elseif (empty($_SESSION['tmp_user_values']['query'][$sql_md5]['pos'])) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['pos'] = 0; + } + + if (PMA_isValid( + $_REQUEST['display_text'], + array( + self::DISPLAY_PARTIAL_TEXT, self::DISPLAY_FULL_TEXT + ) + ) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['display_text'] + = $_REQUEST['display_text']; + unset($_REQUEST['display_text']); + } elseif ( + empty($_SESSION['tmp_user_values']['query'][$sql_md5]['display_text']) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['display_text'] + = self::DISPLAY_PARTIAL_TEXT; + } + + if (PMA_isValid( + $_REQUEST['relational_display'], + array( + self::RELATIONAL_KEY, self::RELATIONAL_DISPLAY_COLUMN + ) + ) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['relational_display'] + = $_REQUEST['relational_display']; + unset($_REQUEST['relational_display']); + } elseif ( + empty( + $_SESSION['tmp_user_values']['query'][$sql_md5]['relational_display'] + ) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['relational_display'] + = self::RELATIONAL_KEY; + } + + if (PMA_isValid( + $_REQUEST['geometry_display'], + array( + self::GEOMETRY_DISP_WKT, self::GEOMETRY_DISP_WKB, + self::GEOMETRY_DISP_GEOM + ) + ) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['geometry_display'] + = $_REQUEST['geometry_display']; + unset($_REQUEST['geometry_display']); + } elseif ( + empty( + $_SESSION['tmp_user_values']['query'][$sql_md5]['geometry_display'] + ) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['geometry_display'] + = self::GEOMETRY_DISP_GEOM; + } + + if (isset($_REQUEST['display_binary'])) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['display_binary'] = true; + unset($_REQUEST['display_binary']); + } elseif (isset($_REQUEST['display_options_form'])) { + // we know that the checkbox was unchecked + unset($_SESSION['tmp_user_values']['query'][$sql_md5]['display_binary']); + } elseif (isset($_REQUEST['full_text_button'])) { + // do nothing to keep the value that is there in the session + } else { + // selected by default because some operations like OPTIMIZE TABLE + // and all queries involving functions return "binary" contents, + // according to low-level field flags + $_SESSION['tmp_user_values']['query'][$sql_md5]['display_binary'] = true; + } + + if (isset($_REQUEST['display_binary_as_hex'])) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['display_binary_as_hex'] + = true; + unset($_REQUEST['display_binary_as_hex']); + } elseif (isset($_REQUEST['display_options_form'])) { + // we know that the checkbox was unchecked + unset($_SESSION['tmp_user_values']['query'][$sql_md5] + ['display_binary_as_hex'] + ); + } elseif (isset($_REQUEST['full_text_button'])) { + // do nothing to keep the value that is there in the session + } else { + // display_binary_as_hex config option + if (isset($GLOBALS['cfg']['DisplayBinaryAsHex']) + && ($GLOBALS['cfg']['DisplayBinaryAsHex'] === true) + ) { + $_SESSION['tmp_user_values']['query'][$sql_md5] + ['display_binary_as_hex'] = true; + } + } + + if (isset($_REQUEST['display_blob'])) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['display_blob'] = true; + unset($_REQUEST['display_blob']); + } elseif (isset($_REQUEST['display_options_form'])) { + // we know that the checkbox was unchecked + unset($_SESSION['tmp_user_values']['query'][$sql_md5]['display_blob']); + } + + if (isset($_REQUEST['hide_transformation'])) { + $_SESSION['tmp_user_values']['query'][$sql_md5]['hide_transformation'] + = true; + unset($_REQUEST['hide_transformation']); + } elseif (isset($_REQUEST['display_options_form'])) { + // we know that the checkbox was unchecked + unset($_SESSION['tmp_user_values']['query'][$sql_md5] + ['hide_transformation'] + ); + } + + // move current query to the last position, to be removed last + // so only least executed query will be removed if maximum remembered queries + // limit is reached + $tmp = $_SESSION['tmp_user_values']['query'][$sql_md5]; + unset($_SESSION['tmp_user_values']['query'][$sql_md5]); + $_SESSION['tmp_user_values']['query'][$sql_md5] = $tmp; + + // do not exceed a maximum number of queries to remember + if (count($_SESSION['tmp_user_values']['query']) > 10) { + array_shift($_SESSION['tmp_user_values']['query']); + //echo 'deleting one element ...'; + } + + // populate query configuration + $_SESSION['tmp_user_values']['display_text'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['display_text']; + $_SESSION['tmp_user_values']['relational_display'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['relational_display']; + $_SESSION['tmp_user_values']['geometry_display'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['geometry_display']; + $_SESSION['tmp_user_values']['display_binary'] + = isset($_SESSION['tmp_user_values']['query'][$sql_md5] + ['display_binary'] + ) + ? true + : false; + $_SESSION['tmp_user_values']['display_binary_as_hex'] + = isset($_SESSION['tmp_user_values']['query'][$sql_md5] + ['display_binary_as_hex'] + ) + ? true + : false; + $_SESSION['tmp_user_values']['display_blob'] + = isset($_SESSION['tmp_user_values']['query'][$sql_md5]['display_blob']) + ? true + : false; + $_SESSION['tmp_user_values']['hide_transformation'] + = isset($_SESSION['tmp_user_values']['query'][$sql_md5] + ['hide_transformation'] + ) + ? true + : false; + $_SESSION['tmp_user_values']['pos'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['pos']; + $_SESSION['tmp_user_values']['max_rows'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['max_rows']; + $_SESSION['tmp_user_values']['repeat_cells'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['repeat_cells']; + $_SESSION['tmp_user_values']['disp_direction'] + = $_SESSION['tmp_user_values']['query'][$sql_md5]['disp_direction']; + + } + + + /** + * Prepare a table of results returned by a SQL query. + * This function is called by the "sql.php" script. + * + * @param integer &$dt_result the link id associated to the query + * which results have to be displayed + * @param array &$the_disp_mode the display mode + * @param array $analyzed_sql the analyzed query + * @param boolean $is_limited_display With limited operations or not + * + * @return string $table_html Generated HTML content for resulted table + * + * @access public + * + * @see sql.php file + */ + public function getTable( + &$dt_result, &$the_disp_mode, $analyzed_sql, $is_limited_display = false + ) { + + $table_html = ''; + // Following variable are needed for use in isset/empty or + // use with array indexes/safe use in foreach + $fields_meta = $this->__get('fields_meta'); + $showtable = $this->__get('showtable'); + $printview = $this->__get('printview'); + + // why was this called here? (already called from sql.php) + //$this->setConfigParamsForDisplayTable(); + + /** + * @todo move this to a central place + * @todo for other future table types + */ + $is_innodb = (isset($showtable['Type']) + && $showtable['Type'] == self::TABLE_TYPE_INNO_DB); + + if ($is_innodb + && ! isset($analyzed_sql[0]['queryflags']['union']) + && ! isset($analyzed_sql[0]['table_ref'][1]['table_name']) + && (empty($analyzed_sql[0]['where_clause']) + || ($analyzed_sql[0]['where_clause'] == '1 ')) + ) { + // "j u s t b r o w s i n g" + $pre_count = '~'; + $after_count = PMA_Util::showHint( + PMA_sanitize( + __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc]') + ) + ); + } else { + $pre_count = ''; + $after_count = ''; + } + + // 1. ----- Prepares the work ----- + + // 1.1 Gets the informations about which functionalities should be + // displayed + $total = ''; + $is_display = $this->_setDisplayMode($the_disp_mode, $total); + + // 1.2 Defines offsets for the next and previous pages + if ($is_display['nav_bar'] == '1') { + list($pos_next, $pos_prev) = $this->_getOffsets(); + } // end if + if (!isset($analyzed_sql[0]['order_by_clause'])) { + $analyzed_sql[0]['order_by_clause'] = ""; + } + + // 1.3 Find the sort expression + // we need $sort_expression and $sort_expression_nodirection + // even if there are many table references + list($sort_expression, $sort_expression_nodirection, $sort_direction) + = $this->_getSortParams($analyzed_sql[0]['order_by_clause']); + + + // 1.4 Prepares display of first and last value of the sorted column + + $sorted_column_message = $this->_getSortedColumnMessage( + $dt_result, $sort_expression_nodirection + ); + + + // 2. ----- Prepare to display the top of the page ----- + + // 2.1 Prepares a messages with position informations + if (($is_display['nav_bar'] == '1') && isset($pos_next)) { + + $message = $this->_setMessageInformation( + $sorted_column_message, $analyzed_sql[0]['limit_clause'], + $total, $pos_next, $pre_count, $after_count + ); + + $table_html .= PMA_Util::getMessage( + $message, $this->__get('sql_query'), 'success' + ); + + } elseif (! isset($printview) || ($printview != '1')) { + + $table_html .= PMA_Util::getMessage( + __('Your SQL query has been executed successfully'), + $this->__get('sql_query'), 'success' + ); + } + + // 2.3 Prepare the navigation bars + if (! strlen($this->__get('table'))) { + + if (isset($analyzed_sql[0]['query_type']) + && ($analyzed_sql[0]['query_type'] == self::QUERY_TYPE_SELECT) + ) { + // table does not always contain a real table name, + // for example in MySQL 5.0.x, the query SHOW STATUS + // returns STATUS as a table name + $this->__set('table', $fields_meta[0]->table); + } else { + $this->__set('table', ''); + } + + } + + if (($is_display['nav_bar'] == '1') + && empty($analyzed_sql[0]['limit_clause']) + ) { + + $table_html .= $this->_getPlacedTableNavigatoins( + $pos_next, $pos_prev, self::PLACE_TOP_DIRECTION_DROPDOWN, + "\n", $is_innodb + ); + + } elseif (! isset($printview) || ($printview != '1')) { + $table_html .= "\n" . '<br /><br />' . "\n"; + } + + // 2b ----- Get field references from Database ----- + // (see the 'relation' configuration variable) + + // initialize map + $map = array(); + + // find tables + $target=array(); + if (isset($analyzed_sql[0]['table_ref']) + && is_array($analyzed_sql[0]['table_ref']) + ) { + + foreach ($analyzed_sql[0]['table_ref'] + as $table_ref_position => $table_ref) { + $target[] = $analyzed_sql[0]['table_ref'] + [$table_ref_position]['table_true_name']; + } + + } + + $tabs = '(\'' . join('\',\'', $target) . '\')'; + + if (! strlen($this->__get('table'))) { + $exist_rel = false; + } else { + // This method set the values for $map array + $this->_setParamForLinkForeignKeyRelatedTables($map); + } // end if + // end 2b + + // 3. ----- Prepare the results table ----- + $table_html .= $this->_getTableHeaders( + $is_display, $analyzed_sql, $sort_expression, + $sort_expression_nodirection, $sort_direction, $is_limited_display + ) + . '<tbody>' . "\n"; + + $url_query = ''; + $table_html .= $this->_getTableBody( + $dt_result, $is_display, $map, $analyzed_sql, $is_limited_display + ); + + // vertical output case + if ($_SESSION['tmp_user_values']['disp_direction'] == self::DISP_DIR_VERTICAL) { + $table_html .= $this->_getVerticalTable($analyzed_sql, $is_display); + } // end if + + $this->__set('vertical_display', null); + + $table_html .= '</tbody>' . "\n" + . '</table>'; + + // 4. ----- Prepares the link for multi-fields edit and delete + + if ($is_display['del_lnk'] == self::DELETE_ROW + && $is_display['del_lnk'] != self::KILL_PROCESS + ) { + + $table_html .= $this->_getMultiRowOperationLinks( + $dt_result, $analyzed_sql, $is_display['del_lnk'] + ); + + } + + // 5. ----- Get the navigation bar at the bottom if required ----- + if (($is_display['nav_bar'] == '1') + && empty($analyzed_sql[0]['limit_clause']) + ) { + $table_html .= $this->_getPlacedTableNavigatoins( + $pos_next, $pos_prev, self::PLACE_BOTTOM_DIRECTION_DROPDOWN, + '<br />' . "\n", $is_innodb + ); + } elseif (! isset($printview) || ($printview != '1')) { + $table_html .= "\n" . '<br /><br />' . "\n"; + } + + + // 6. ----- Prepare "Query results operations" + if ((! isset($printview) || ($printview != '1')) && ! $is_limited_display) { + $table_html .= $this->_getResultsOperations( + $the_disp_mode, $analyzed_sql + ); + } + + return $table_html; + + } // end of the 'getTable()' function + + + /** + * Get offsets for next page and previous page + * + * @return array array with two elements - $pos_next, $pos_prev + * + * @access private + * + * @see getTable() + */ + private function _getOffsets() + { + + if ($_SESSION['tmp_user_values']['max_rows'] == self::ALL_ROWS) { + $pos_next = 0; + $pos_prev = 0; + } else { + + $pos_next = $_SESSION['tmp_user_values']['pos'] + + $_SESSION['tmp_user_values']['max_rows']; + + $pos_prev = $_SESSION['tmp_user_values']['pos'] + - $_SESSION['tmp_user_values']['max_rows']; + + if ($pos_prev < 0) { + $pos_prev = 0; + } + } + + return array($pos_next, $pos_prev); + + } // end of the '_getOffsets()' function + + + /** + * Get sort parameters + * + * @param string $order_by_clause the order by clause of the sql query + * + * @return array 3 element array: $sort_expression, + * $sort_expression_nodirection, $sort_direction + * + * @access private + * + * @see getTable() + */ + private function _getSortParams($order_by_clause) + { + + if (! empty($order_by_clause)) { + + $sort_expression = trim( + str_replace(' ', ' ', $order_by_clause) + ); + /** + * Get rid of ASC|DESC + */ + preg_match( + '@(.*)([[:space:]]*(ASC|DESC))@si', $sort_expression, $matches + ); + + $sort_expression_nodirection = isset($matches[1]) + ? trim($matches[1]) + : $sort_expression; + + $sort_direction = isset($matches[2]) ? trim($matches[2]) : ''; + unset($matches); + + } else { + $sort_expression = $sort_expression_nodirection = $sort_direction = ''; + } + + return array($sort_expression, $sort_expression_nodirection, + $sort_direction + ); + + } // end of the '_getSortParams()' function + + + /** + * Prepare sorted column message + * + * @param integer &$dt_result the link id associated to the + * query which results have to + * be displayed + * @param string $sort_expression_nodirection sort expression without direction + * + * @return string html content + * null if not found sorted column + * + * @access private + * + * @see getTable() + */ + private function _getSortedColumnMessage( + &$dt_result, $sort_expression_nodirection + ) { + + $fields_meta = $this->__get('fields_meta'); // To use array indexes + + if (! empty($sort_expression_nodirection)) { + + if (strpos($sort_expression_nodirection, '.') === false) { + $sort_table = $this->__get('table'); + $sort_column = $sort_expression_nodirection; + } else { + list($sort_table, $sort_column) + = explode('.', $sort_expression_nodirection); + } + + $sort_table = PMA_Util::unQuote($sort_table); + $sort_column = PMA_Util::unQuote($sort_column); + + // find the sorted column index in row result + // (this might be a multi-table query) + $sorted_column_index = false; + + foreach ($fields_meta as $key => $meta) { + if (($meta->table == $sort_table) && ($meta->name == $sort_column)) { + $sorted_column_index = $key; + break; + } + } + + if ($sorted_column_index !== false) { + + // fetch first row of the result set + $row = PMA_DBI_fetch_row($dt_result); + + // initializing default arguments + $default_function = '_mimeDefaultFunction'; + $transformation_plugin = $default_function; + $transform_options = array(); + + // check for non printable sorted row data + $meta = $fields_meta[$sorted_column_index]; + + if (stristr($meta->type, self::BLOB_FIELD) + || ($meta->type == self::GEOMETRY_FIELD) + ) { + + $column_for_first_row = $this->_handleNonPrintableContents( + $meta->type, $row[$sorted_column_index], + $transformation_plugin, $transform_options, + $default_function, $meta, null + ); + + } else { + $column_for_first_row = $row[$sorted_column_index]; + } + + $column_for_first_row = strtoupper( + substr($column_for_first_row, 0, $GLOBALS['cfg']['LimitChars']) + ); + + // fetch last row of the result set + PMA_DBI_data_seek($dt_result, $this->__get('num_rows') - 1); + $row = PMA_DBI_fetch_row($dt_result); + + // check for non printable sorted row data + $meta = $fields_meta[$sorted_column_index]; + if (stristr($meta->type, self::BLOB_FIELD) + || ($meta->type == self::GEOMETRY_FIELD) + ) { + + $column_for_last_row = $this->_handleNonPrintableContents( + $meta->type, $row[$sorted_column_index], + $transformation_plugin, $transform_options, + $default_function, $meta, null + ); + + } else { + $column_for_last_row = $row[$sorted_column_index]; + } + + $column_for_last_row = strtoupper( + substr($column_for_last_row, 0, $GLOBALS['cfg']['LimitChars']) + ); + + // reset to first row for the loop in _getTableBody() + PMA_DBI_data_seek($dt_result, 0); + + // we could also use here $sort_expression_nodirection + return ' [' . htmlspecialchars($sort_column) + . ': <strong>' . htmlspecialchars($column_for_first_row) . ' - ' + . htmlspecialchars($column_for_last_row) . '</strong>]'; + } + } + + return null; + + } // end of the '_getSortedColumnMessage()' function + + + /** + * Set the content need to be show in message + * + * @param string $sorted_column_message the message for sorted column + * @param string $limit_clause the limit clause of analyzed query + * @param integer $total the total number of rows returned by + * the SQL query without any + * programmatically appended LIMIT clause + * @param integer $pos_next the offset for next page + * @param string $pre_count the string renders before row count + * @param string $after_count the string renders after row count + * + * @return PMA_Message $message an object of PMA_Message + * + * @access private + * + * @see getTable() + */ + private function _setMessageInformation( + $sorted_column_message, $limit_clause, $total, + $pos_next, $pre_count, $after_count + ) { + + $unlim_num_rows = $this->__get('unlim_num_rows'); // To use in isset() + + if (isset($unlim_num_rows) && ($unlim_num_rows != $total)) { + $selectstring = ', ' . $unlim_num_rows . ' ' . __('in query'); + } else { + $selectstring = ''; + } + + if (! empty($limit_clause)) { + + $limit_data + = PMA_Util::analyzeLimitClause($limit_clause); + $first_shown_rec = $limit_data['start']; + + if ($limit_data['length'] < $total) { + $last_shown_rec = $limit_data['start'] + $limit_data['length'] - 1; + } else { + $last_shown_rec = $limit_data['start'] + $total - 1; + } + + } elseif (($_SESSION['tmp_user_values']['max_rows'] == self::ALL_ROWS) + || ($pos_next > $total) + ) { + + $first_shown_rec = $_SESSION['tmp_user_values']['pos']; + $last_shown_rec = $total - 1; + + } else { + + $first_shown_rec = $_SESSION['tmp_user_values']['pos']; + $last_shown_rec = $pos_next - 1; + + } + + if (PMA_Table::isView($this->__get('db'), $this->__get('table')) + && ($total == $GLOBALS['cfg']['MaxExactCountViews']) + ) { + + $message = PMA_Message::notice( + __( + 'This view has at least this number of rows. ' + . 'Please refer to %sdocumentation%s.' + ) + ); + + $message->addParam('[doc@cfg_MaxExactCount]'); + $message->addParam('[/doc]'); + $message_view_warning = PMA_Util::showHint($message); + + } else { + $message_view_warning = false; + } + + $message = PMA_Message::success(__('Showing rows %1s - %2s')); + $message->addParam($first_shown_rec); + + if ($message_view_warning) { + $message->addParam('... ' . $message_view_warning, false); + } else { + $message->addParam($last_shown_rec); + } + + $message->addMessage('('); + + if (!$message_view_warning) { + $message_total = PMA_Message::notice($pre_count . __('%d total')); + $message_total->addParam($total); + + if (!empty($after_count)) { + $message_total->addMessage($after_count); + } + $message->addMessage($message_total, ''); + + $message->addMessage($selectstring, ''); + $message->addMessage(', ', ''); + } + + $messagge_qt = PMA_Message::notice(__('Query took %01.4f sec') . ')'); + $messagge_qt->addParam($this->__get('querytime')); + + $message->addMessage($messagge_qt, ''); + if (! is_null($sorted_column_message)) { + $message->addMessage($sorted_column_message, ''); + } + + return $message; + + } // end of the '_setMessageInformation()' function + + + /** + * Set the value of $map array for linking foreign key related tables + * + * @param array &$map the list of relations + * + * @return void + * + * @access private + * + * @see getTable() + */ + private function _setParamForLinkForeignKeyRelatedTables(&$map) + { + + // To be able to later display a link to the related table, + // we verify both types of relations: either those that are + // native foreign keys or those defined in the phpMyAdmin + // configuration storage. If no PMA storage, we won't be able + // to use the "column to display" notion (for example show + // the name related to a numeric id). + $exist_rel = PMA_getForeigners( + $this->__get('db'), $this->__get('table'), '', self::POSITION_BOTH + ); + + if ($exist_rel) { + + foreach ($exist_rel as $master_field => $rel) { + + $display_field = PMA_getDisplayField( + $rel['foreign_db'], $rel['foreign_table'] + ); + + $map[$master_field] = array( + $rel['foreign_table'], + $rel['foreign_field'], + $display_field, + $rel['foreign_db'] + ); + } // end while + } // end if + + } // end of the '_setParamForLinkForeignKeyRelatedTables()' function + + + /** + * Prepare multi field edit/delete links + * + * @param integer &$dt_result the link id associated to the query + * which results have to be displayed + * @param array $analyzed_sql the analyzed query + * @param string $del_link the display element - 'del_link' + * + * @return string $links_html html content + * + * @access private + * + * @see getTable() + */ + private function _getMultiRowOperationLinks( + &$dt_result, $analyzed_sql, $del_link + ) { + + $links_html = ''; + $url_query = $this->__get('url_query'); + $delete_text = ($del_link == self::DELETE_ROW) ? __('Delete') : __('Kill'); + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $this->__get('sql_query'), + 'goto' => $this->__get('goto'), + ); + + if ($_SESSION['tmp_user_values']['disp_direction'] != self::DISP_DIR_VERTICAL) { + + $links_html .= '<img class="selectallarrow" width="38" height="22"' + . ' src="' . $this->__get('pma_theme_image') . 'arrow_' + . $this->__get('text_dir') . '.png' . '"' + . ' alt="' . __('With selected:') . '" />'; + } + + $links_html .= '<input type="checkbox" id="checkall" title="' + . __('Check All') . '" /> ' + . '<label for="checkall">' . __('Check All') . '</label> ' + . '<i style="margin-left: 2em">' . __('With selected:') . '</i>' . "\n"; + + $links_html .= PMA_Util::getButtonOrImage( + 'submit_mult', 'mult_submit', 'submit_mult_change', + __('Change'), 'b_edit.png', 'edit' + ); + + $links_html .= PMA_Util::getButtonOrImage( + 'submit_mult', 'mult_submit', 'submit_mult_delete', + $delete_text, 'b_drop.png', 'delete' + ); + + if (isset($analyzed_sql[0]) + && $analyzed_sql[0]['querytype'] == self::QUERY_TYPE_SELECT + ) { + $links_html .= PMA_Util::getButtonOrImage( + 'submit_mult', 'mult_submit', 'submit_mult_export', + __('Export'), 'b_tblexport.png', 'export' + ); + } + + $links_html .= "\n"; + + $links_html .= '<input type="hidden" name="sql_query"' + .' value="' . htmlspecialchars($this->__get('sql_query')) . '" />' + . "\n"; + + if (! empty($url_query)) { + $links_html .= '<input type="hidden" name="url_query"' + .' value="' . $url_query . '" />' . "\n"; + } + + // fetch last row of the result set + PMA_DBI_data_seek($dt_result, $this->__get('num_rows') - 1); + $row = PMA_DBI_fetch_row($dt_result); + + // $clause_is_unique is needed by getTable() to generate the proper param + // in the multi-edit and multi-delete form + list($where_clause, $clause_is_unique, $condition_array) + = PMA_Util::getUniqueCondition( + $dt_result, + $this->__get('fields_cnt'), + $this->__get('fields_meta'), + $row + ); + + // reset to first row for the loop in _getTableBody() + PMA_DBI_data_seek($dt_result, 0); + + $links_html .= '<input type="hidden" name="clause_is_unique"' + .' value="' . $clause_is_unique . '" />' . "\n"; + + $links_html .= '</form>' . "\n"; + + return $links_html; + + } // end of the '_getMultiRowOperationLinks()' function + + + /** + * Prepare table navigation bar at the top or bottom + * + * @param integer $pos_next the offset for the "next" page + * @param integer $pos_prev the offset for the "previous" page + * @param string $place the place to show navigation + * @param string $empty_line empty line depend on the $place + * @param boolean $is_innodb whether its InnoDB or not + * + * @return string html content of navigation bar + * + * @access private + * + * @see _getTable() + */ + private function _getPlacedTableNavigatoins( + $pos_next, $pos_prev, $place, $empty_line, $is_innodb + ) { + + $navigation_html = ''; + + if ($place == self::PLACE_BOTTOM_DIRECTION_DROPDOWN) { + $navigation_html .= '<br />' . "\n"; + } + + $navigation_html .= $this->_getTableNavigation( + $pos_next, $pos_prev, 'top_direction_dropdown', $is_innodb + ); + + if ($place == self::PLACE_TOP_DIRECTION_DROPDOWN) { + $navigation_html .= "\n"; + } + + return $navigation_html; + + } // end of the '_getPlacedTableNavigatoins()' function + + + /** + * Get operations that are available on results. + * + * @param array $the_disp_mode the display mode + * @param array $analyzed_sql the analyzed query + * + * @return string $results_operations_html html content + * + * @access private + * + * @see getTable() + */ + private function _getResultsOperations($the_disp_mode, $analyzed_sql) + { + global $printview; + + $results_operations_html = ''; + $fields_meta = $this->__get('fields_meta'); // To safe use in foreach + $header_shown = false; + $header = '<fieldset><legend>' . __('Query results operations') + . '</legend>'; + + if (($the_disp_mode[6] == '1') || ($the_disp_mode[9] == '1')) { + // Displays "printable view" link if required + if ($the_disp_mode[9] == '1') { + + if (!$header_shown) { + $results_operations_html .= $header; + $header_shown = true; + } + + $_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'printview' => '1', + 'sql_query' => $this->__get('sql_query'), + ); + $url_query = PMA_generate_common_url($_url_params); + + $results_operations_html + .= PMA_Util::linkOrButton( + 'sql.php' . $url_query, + PMA_Util::getIcon( + 'b_print.png', __('Print view'), true + ), + array('target' => 'print_view'), + true, + true, + 'print_view' + ) + . "\n"; + + if ($_SESSION['tmp_user_values']['display_text']) { + + $_url_params['display_text'] = self::DISPLAY_FULL_TEXT; + + $results_operations_html + .= PMA_Util::linkOrButton( + 'sql.php' . PMA_generate_common_url($_url_params), + PMA_Util::getIcon( + 'b_print.png', + __('Print view (with full texts)'), true + ), + array('target' => 'print_view'), + true, + true, + 'print_view' + ) + . "\n"; + unset($_url_params['display_text']); + } + } // end displays "printable view" + } + + // Export link + // (the url_query has extra parameters that won't be used to export) + // (the single_table parameter is used in display_export.lib.php + // to hide the SQL and the structure export dialogs) + // If the parser found a PROCEDURE clause + // (most probably PROCEDURE ANALYSE()) it makes no sense to + // display the Export link). + if (isset($analyzed_sql[0]) + && ($analyzed_sql[0]['querytype'] == self::QUERY_TYPE_SELECT) + && ! isset($printview) + && ! isset($analyzed_sql[0]['queryflags']['procedure']) + ) { + + if (isset($analyzed_sql[0]['table_ref'][0]['table_true_name']) + && ! isset($analyzed_sql[0]['table_ref'][1]['table_true_name']) + ) { + $_url_params['single_table'] = 'true'; + } + + if (! $header_shown) { + $results_operations_html .= $header; + $header_shown = true; + } + + $_url_params['unlim_num_rows'] = $this->__get('unlim_num_rows'); + + /** + * At this point we don't know the table name; this can happen + * for example with a query like + * SELECT bike_code FROM (SELECT bike_code FROM bikes) tmp + * As a workaround we set in the table parameter the name of the + * first table of this database, so that tbl_export.php and + * the script it calls do not fail + */ + if (empty($_url_params['table']) && ! empty($_url_params['db'])) { + $_url_params['table'] = PMA_DBI_fetch_value("SHOW TABLES"); + /* No result (probably no database selected) */ + if ($_url_params['table'] === false) { + unset($_url_params['table']); + } + } + + $results_operations_html .= PMA_Util::linkOrButton( + 'tbl_export.php' . PMA_generate_common_url($_url_params), + PMA_Util::getIcon( + 'b_tblexport.png', __('Export'), true + ), + '', + true, + true, + '' + ) + . "\n"; + + // prepare chart + $results_operations_html .= PMA_Util::linkOrButton( + 'tbl_chart.php' . PMA_generate_common_url($_url_params), + PMA_Util::getIcon( + 'b_chart.png', __('Display chart'), true + ), + '', + true, + true, + '' + ) + . "\n"; + + // prepare GIS chart + $geometry_found = false; + // If atleast one geometry field is found + foreach ($fields_meta as $meta) { + if ($meta->type == self::GEOMETRY_FIELD) { + $geometry_found = true; + break; + } + } + + if ($geometry_found) { + $results_operations_html + .= PMA_Util::linkOrButton( + 'tbl_gis_visualization.php' + . PMA_generate_common_url($_url_params), + PMA_Util::getIcon( + 'b_globe.gif', __('Visualize GIS data'), true + ), + '', + true, + true, + '' + ) + . "\n"; + } + } + + // CREATE VIEW + /** + * + * @todo detect privileges to create a view + * (but see 2006-01-19 note in display_create_table.lib.php, + * I think we cannot detect db-specific privileges reliably) + * Note: we don't display a Create view link if we found a PROCEDURE clause + */ + if (!$header_shown) { + $results_operations_html .= $header; + $header_shown = true; + } + + if (!PMA_DRIZZLE && !isset($analyzed_sql[0]['queryflags']['procedure'])) { + + $ajax_class = ' ajax'; + + $results_operations_html .= '<span>' + . PMA_Util::linkOrButton( + 'view_create.php' . $url_query, + PMA_Util::getIcon( + 'b_views.png', __('Create view'), true + ), + array('class' => 'create_view' . $ajax_class), true, true, '' + ) + . '</span>' . "\n"; + } + + if ($header_shown) { + $results_operations_html .= '</fieldset><br />'; + } + + return $results_operations_html; + + } // end of the '_getResultsOperations()' function + + + /** + * Verifies what to do with non-printable contents (binary or BLOB) + * in Browse mode. + * + * @param string $category BLOB|BINARY|GEOMETRY + * @param string $content the binary content + * @param string $transformation_plugin transformation plugin. + * Can also be the default function: + * PMA_mimeDefaultFunction + * @param string $transform_options transformation parameters + * @param string $default_function default transformation function + * @param object $meta the meta-information about the field + * @param array $url_params parameters that should go to the + * download link + * + * @return mixed string or float + * + * @access private + * + * @see _getDataCellForBlobColumns(), + * _getDataCellForGeometryColumns(), + * _getDataCellForNonNumericAndNonBlobColumns(), + * _getSortedColumnMessage() + */ + private function _handleNonPrintableContents( + $category, $content, $transformation_plugin, $transform_options, + $default_function, $meta, $url_params = array() + ) { + + $result = '[' . $category; + + if (is_null($content)) { + + $result .= ' - NULL'; + $size = 0; + + } elseif (isset($content)) { + + $size = strlen($content); + $display_size + = PMA_Util::formatByteDown($size, 3, 1); + $result .= ' - '. $display_size[0] . ' ' . $display_size[1]; + + } + + $result .= ']'; + + if (gettype($transformation_plugin) == "object" + && (strpos($transformation_plugin->getMIMESubtype(), 'Octetstream') + // if we want to use a text transformation on a BLOB column + || strpos($transformation_plugin->getMIMEtype(), 'Text') !== false) + ) { + $result = $content; + } + + if ($size > 0) { + + if ($default_function != $transformation_plugin) { + $result = $transformation_plugin->applyTransformation( + $result, + $transform_options, + $meta + ); + } else { + + $result = $this->$default_function($result, array(), $meta); + if (stristr($meta->type, self::BLOB_FIELD) + && $_SESSION['tmp_user_values']['display_blob'] + ) { + // in this case, restart from the original $content + $result = $this->_displayBinaryAsPrintable($content, 'blob'); + } + + /* Create link to download */ + if (count($url_params) > 0) { + $result = '<a href="tbl_get_field.php' + . PMA_generate_common_url($url_params) . '" class="disableAjax">' + . $result . '</a>'; + } + } + } + + return($result); + + } // end of the '_handleNonPrintableContents()' function + + + /** + * Prepares the displayable content of a data cell in Browse mode, + * taking into account foreign key description field and transformations + * + * @param string $class css classes for the td element + * @param bool $condition_field whether the column is a part of the + * where clause + * @param string $analyzed_sql the analyzed query + * @param object $meta the meta-information about the field + * @param array $map the list of relations + * @param string $data data + * @param string $transformation_plugin transformation plugin. + * Can also be the default function: + * PMA_mimeDefaultFunction + * @param string $default_function default function + * @param string $nowrap 'nowrap' if the content should not + * be wrapped + * @param string $where_comparison data for the where clause + * @param array $transform_options array of options for transformation + * @param bool $is_field_truncated whether the field is truncated + * + * @return string formatted data + * + * @access private + * + * @see _getDataCellForNumericColumns(), _getDataCellForGeometryColumns(), + * _getDataCellForNonNumericAndNonBlobColumns(), + * + */ + private function _getRowData( + $class, $condition_field, $analyzed_sql, $meta, $map, $data, + $transformation_plugin, $default_function, $nowrap, $where_comparison, + $transform_options, $is_field_truncated + ) { + + $printview = $this->__get('printview'); + $result = '<td class="' + . $this->_addClass( + $class, $condition_field, $meta, $nowrap, + $is_field_truncated, $transformation_plugin, $default_function + ) + . '">'; + + if (isset($analyzed_sql[0]['select_expr']) + && is_array($analyzed_sql[0]['select_expr']) + ) { + + foreach ($analyzed_sql[0]['select_expr'] + as $select_expr_position => $select_expr + ) { + + $alias = $analyzed_sql[0]['select_expr'] + [$select_expr_position]['alias']; + + if (isset($alias) && strlen($alias)) { + $true_column = $analyzed_sql[0]['select_expr'] + [$select_expr_position]['column']; + + if ($alias == $meta->name) { + // this change in the parameter does not matter + // outside of the function + $meta->name = $true_column; + } // end if + + } // end if + } // end foreach + } // end if + + if (isset($map[$meta->name])) { + + // Field to display from the foreign table? + if (isset($map[$meta->name][2]) && strlen($map[$meta->name][2])) { + + $dispsql = 'SELECT ' + . PMA_Util::backquote($map[$meta->name][2]) + . ' FROM ' + . PMA_Util::backquote($map[$meta->name][3]) + . '.' + . PMA_Util::backquote($map[$meta->name][0]) + . ' WHERE ' + . PMA_Util::backquote($map[$meta->name][1]) + . $where_comparison; + + $dispresult = PMA_DBI_try_query($dispsql, null, PMA_DBI_QUERY_STORE); + + if ($dispresult && PMA_DBI_num_rows($dispresult) > 0) { + list($dispval) = PMA_DBI_fetch_row($dispresult, 0); + } else { + $dispval = __('Link not found'); + } + + @PMA_DBI_free_result($dispresult); + + } else { + $dispval = ''; + } // end if... else... + + if (isset($printview) && ($printview == '1')) { + + $result .= ($transformation_plugin != $default_function + ? $transformation_plugin->applyTransformation( + $data, + $transform_options, + $meta + ) + : $this->$default_function($data) + ) + . ' <code>[->' . $dispval . ']</code>'; + + } else { + + if ($_SESSION['tmp_user_values']['relational_display'] == self::RELATIONAL_KEY) { + + // user chose "relational key" in the display options, so + // the title contains the display field + $title = (! empty($dispval)) + ? ' title="' . htmlspecialchars($dispval) . '"' + : ''; + + } else { + $title = ' title="' . htmlspecialchars($data) . '"'; + } + + $_url_params = array( + 'db' => $map[$meta->name][3], + 'table' => $map[$meta->name][0], + 'pos' => '0', + 'sql_query' => 'SELECT * FROM ' + . PMA_Util::backquote( + $map[$meta->name][3] + ) . '.' + . PMA_Util::backquote( + $map[$meta->name][0] + ) + . ' WHERE ' + . PMA_Util::backquote( + $map[$meta->name][1] + ) + . $where_comparison, + ); + + $result .= '<a class="ajax" href="sql.php' . PMA_generate_common_url($_url_params) + . '"' . $title . '>'; + + if ($transformation_plugin != $default_function) { + // always apply a transformation on the real data, + // not on the display field + $result .= $transformation_plugin->applyTransformation( + $data, + $transform_options, + $meta + ); + } else { + + if ($_SESSION['tmp_user_values']['relational_display'] == self::RELATIONAL_DISPLAY_COLUMN) { + // user chose "relational display field" in the + // display options, so show display field in the cell + $result .= $this->$default_function($dispval); + } else { + // otherwise display data in the cell + $result .= $this->$default_function($data); + } + + } + $result .= '</a>'; + } + + } else { + $result .= ($transformation_plugin != $default_function + ? $transformation_plugin->applyTransformation( + $data, + $transform_options, + $meta + ) + : $this->$default_function($data) + ); + } + + // create hidden field if results from structure table + if (isset($_GET['browse_distinct']) && ($_GET['browse_distinct'] == 1)) { + + $where_comparison = " = '" . $data . "'"; + + $_url_params_for_show_data_row = array( + 'db' => $this->__get('db'), + 'table' => $meta->orgtable, + 'pos' => '0', + 'sql_query' => 'SELECT * FROM ' + . PMA_Util::backquote($this->__get('db')) + . '.' . PMA_Util::backquote($meta->orgtable) + . ' WHERE ' + . PMA_Util::backquote($meta->orgname) + . $where_comparison, + ); + + $result .= '<input type="hidden" class="data_browse_link" value="' + . PMA_generate_common_url($_url_params_for_show_data_row). '" />'; + + } + + $result .= '</td>' . "\n"; + + return $result; + + } // end of the '_getRowData()' function + + + /** + * Prepares a checkbox for multi-row submits + * + * @param string $del_url delete url + * @param array $is_display array with explicit indexes for all + * the display elements + * @param string $row_no the row number + * @param string $where_clause_html url encoded where clause + * @param array $condition_array array of conditions in the where clause + * @param string $del_query delete query + * @param string $id_suffix suffix for the id + * @param string $class css classes for the td element + * + * @return string the generated HTML + * + * @access private + * + * @see _getTableBody(), _getCheckboxAndLinks() + */ + private function _getCheckboxForMultiRowSubmissions( + $del_url, $is_display, $row_no, $where_clause_html, $condition_array, + $del_query, $id_suffix, $class + ) { + + $ret = ''; + + if (! empty($del_url) && $is_display['del_lnk'] != self::KILL_PROCESS) { + + $ret .= '<td '; + if (! empty($class)) { + $ret .= 'class="' . $class . '"'; + } + + $ret .= ' class="center">' + . '<input type="checkbox" id="id_rows_to_delete' + . $row_no . $id_suffix + . '" name="rows_to_delete[' . $row_no . ']"' + . ' class="multi_checkbox checkall"' + . ' value="' . $where_clause_html . '" ' + . ' />' + . '<input type="hidden" class="condition_array" value="' + . htmlspecialchars(json_encode($condition_array)) . '" />' + . ' </td>'; + } + + return $ret; + + } // end of the '_getCheckboxForMultiRowSubmissions()' function + + + /** + * Prepares an Edit link + * + * @param string $edit_url edit url + * @param string $class css classes for td element + * @param string $edit_str text for the edit link + * @param string $where_clause where clause + * @param string $where_clause_html url encoded where clause + * + * @return string the generated HTML + * + * @access private + * + * @see _getTableBody(), _getCheckboxAndLinks() + */ + private function _getEditLink( + $edit_url, $class, $edit_str, $where_clause, $where_clause_html + ) { + + $ret = ''; + if (! empty($edit_url)) { + + $ret .= '<td class="' . $class . ' center" ' . ' ><span class="nowrap">' + . PMA_Util::linkOrButton( + $edit_url, $edit_str, array(), false + ); + /* + * Where clause for selecting this row uniquely is provided as + * a hidden input. Used by jQuery scripts for handling grid editing + */ + if (! empty($where_clause)) { + $ret .= '<input type="hidden" class="where_clause" value ="' + . $where_clause_html . '" />'; + } + $ret .= '</span></td>'; + } + + return $ret; + + } // end of the '_getEditLink()' function + + + /** + * Prepares an Copy link + * + * @param string $copy_url copy url + * @param string $copy_str text for the copy link + * @param string $where_clause where clause + * @param string $where_clause_html url encoded where clause + * @param string $class css classes for the td element + * + * @return string the generated HTML + * + * @access private + * + * @see _getTableBody(), _getCheckboxAndLinks() + */ + private function _getCopyLink( + $copy_url, $copy_str, $where_clause, $where_clause_html, $class + ) { + + $ret = ''; + if (! empty($copy_url)) { + + $ret .= '<td class="'; + if (! empty($class)) { + $ret .= $class . ' '; + } + + $ret .= 'center" ' . ' ><span class="nowrap">' + . PMA_Util::linkOrButton( + $copy_url, $copy_str, array(), false + ); + + /* + * Where clause for selecting this row uniquely is provided as + * a hidden input. Used by jQuery scripts for handling grid editing + */ + if (! empty($where_clause)) { + $ret .= '<input type="hidden" class="where_clause" value="' + . $where_clause_html . '" />'; + } + $ret .= '</span></td>'; + } + + return $ret; + + } // end of the '_getCopyLink()' function + + + /** + * Prepares a Delete link + * + * @param string $del_url delete url + * @param string $del_str text for the delete link + * @param string $js_conf text for the JS confirmation + * @param string $class css classes for the td element + * + * @return string the generated HTML + * + * @access private + * + * @see _getTableBody(), _getCheckboxAndLinks() + */ + private function _getDeleteLink($del_url, $del_str, $js_conf, $class) + { + + $ret = ''; + if (! empty($del_url)) { + + $ret .= '<td class="'; + if (! empty($class)) { + $ret .= $class . ' '; + } + $ajax = PMA_Response::getInstance()->isAjax() ? ' ajax' : ''; + $ret .= 'center" ' . ' >' + . PMA_Util::linkOrButton( + $del_url, $del_str, array('class' => 'delete_row' . $ajax), false + ) + . '<div class="hide">' . $js_conf . '</div>' + . '</td>'; + } + + return $ret; + + } // end of the '_getDeleteLink()' function + + + /** + * Prepare checkbox and links at some position (left or right) + * (only called for horizontal mode) + * + * @param string $position the position of the checkbox and links + * @param string $del_url delete url + * @param array $is_display array with explicit indexes for all the + * display elements + * @param string $row_no row number + * @param string $where_clause where clause + * @param string $where_clause_html url encoded where clause + * @param array $condition_array array of conditions in the where clause + * @param string $del_query delete query + * @param string $id_suffix suffix for the id + * @param string $edit_url edit url + * @param string $copy_url copy url + * @param string $class css classes for the td elements + * @param string $edit_str text for the edit link + * @param string $copy_str text for the copy link + * @param string $del_str text for the delete link + * @param string $js_conf text for the JS confirmation + * + * @return string the generated HTML + * + * @access private + * + * @see _getPlacedLinks() + */ + private function _getCheckboxAndLinks( + $position, $del_url, $is_display, $row_no, $where_clause, + $where_clause_html, $condition_array, $del_query, $id_suffix, + $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf + ) { + + $ret = ''; + + if ($position == self::POSITION_LEFT) { + + $ret .= $this->_getCheckboxForMultiRowSubmissions( + $del_url, $is_display, $row_no, $where_clause_html, $condition_array, + $del_query, $id_suffix = '_left', '' + ); + + $ret .= $this->_getEditLink( + $edit_url, $class, $edit_str, $where_clause, $where_clause_html + ); + + $ret .= $this->_getCopyLink( + $copy_url, $copy_str, $where_clause, $where_clause_html, '' + ); + + $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, ''); + + } elseif ($position == self::POSITION_RIGHT) { + + $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, ''); + + $ret .= $this->_getCopyLink( + $copy_url, $copy_str, $where_clause, $where_clause_html, '' + ); + + $ret .= $this->_getEditLink( + $edit_url, $class, $edit_str, $where_clause, $where_clause_html + ); + + $ret .= $this->_getCheckboxForMultiRowSubmissions( + $del_url, $is_display, $row_no, $where_clause_html, $condition_array, + $del_query, $id_suffix = '_right', '' + ); + + } else { // $position == self::POSITION_NONE + + $ret .= $this->_getCheckboxForMultiRowSubmissions( + $del_url, $is_display, $row_no, $where_clause_html, $condition_array, + $del_query, $id_suffix = '_left', '' + ); + } + + return $ret; + + } // end of the '_getCheckboxAndLinks()' function + + + /** + * Replace some html-unfriendly stuff + * + * @param string $buffer String to process + * + * @return Escaped and cleaned up text suitable for html. + * + * @access private + * + * @see _getDataCellForBlobField(), _getRowData(), + * _handleNonPrintableContents() + */ + private function _mimeDefaultFunction($buffer) + { + $buffer = htmlspecialchars($buffer); + $buffer = str_replace( + "\011", + ' ', + str_replace(' ', ' ', $buffer) + ); + $buffer = preg_replace("@((\015\012)|(\015)|(\012))@", '<br />', $buffer); + + return $buffer; + } + + /** + * Display binary fields as hex string for PHP <5.4, + * otherwise escape the contents if it may be displayed as hex + * + * @param string $content String to parse + * @param string $binary_or_blob 'binary' or 'blob' + * @param int $hexlength optional, get substring + * + * @return Displayable version of the binary string + * + * @access private + * + * @see _getDataCellForGeometryColumns + * _getDataCellForNonNumericAndNonBlobColumns + * _handleNonPrintableContents + */ + private function _displayBinaryAsPrintable( + $content, $binary_or_blob, $hexlength = null + ) { + if (PMA_PHP_INT_VERSION < 50400 + || ($binary_or_blob === 'binary' + && $_SESSION['tmp_user_values']['display_binary_as_hex'] + && PMA_Util::containsNonPrintableAscii($content) + ) + ) { + $content = bin2hex($content); + if ($hexlength !== null) { + $content = PMA_substr($content, $hexlength); + } + } else { + $content = htmlspecialchars( + PMA_Util::replaceBinaryContents( + $content + ), + ENT_SUBSTITUTE + ); + } + return $content; + } +} +?> |
