with PHP itself instead of JavaScript.
+ *
+ * @todo find a better solution that uses $.append(), the recommended
+ * method maybe by using an additional param, the id of the div to
+ * append to
+ */
+
+ return '
';
+
+ }
+
+
+ /**
+ * Creates an AJAX sliding toggle button
+ * (or and equivalent form when AJAX is disabled)
+ *
+ * @param string $action The URL for the request to be executed
+ * @param string $select_name The name for the dropdown box
+ * @param array $options An array of options (see rte_footer.lib.php)
+ * @param string $callback A JS snippet to execute when the request is
+ * successfully processed
+ *
+ * @return string HTML code for the toggle button
+ */
+ public function toggleButton($action, $select_name, $options, $callback)
+ {
+ // Do the logic first
+ $link = "$action&" . urlencode($select_name) . "=";
+ $link_on = $link . urlencode($options[1]['value']);
+ $link_off = $link . urlencode($options[0]['value']);
+
+ if ($options[1]['selected'] == true) {
+ $state = 'on';
+ } else if ($options[0]['selected'] == true) {
+ $state = 'off';
+ } else {
+ $state = 'on';
+ }
+
+ // Generate output
+ return "\n"
+ . "
\n"
+ . "";
+
+ } // end toggleButton()
+
+
+ /**
+ * Clears cache content which needs to be refreshed on user change.
+ *
+ * @return void
+ */
+ public function clearUserCache()
+ {
+ $this->cacheUnset('is_superuser', true);
+ }
+
+
+ /**
+ * Verifies if something is cached in the session
+ *
+ * @param string $var variable name
+ * @param int|true $server server
+ *
+ * @return boolean
+ */
+ public function cacheExists($var, $server = 0)
+ {
+ if ($server === true) {
+ $server = $GLOBALS['server'];
+ }
+ return isset($_SESSION['cache']['server_' . $server][$var]);
+ }
+
+
+ /**
+ * Gets cached information from the session
+ *
+ * @param string $var varibale name
+ * @param int|true $server server
+ *
+ * @return mixed
+ */
+ public function cacheGet($var, $server = 0)
+ {
+ if ($server === true) {
+ $server = $GLOBALS['server'];
+ }
+ if (isset($_SESSION['cache']['server_' . $server][$var])) {
+ return $_SESSION['cache']['server_' . $server][$var];
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Caches information in the session
+ *
+ * @param string $var variable name
+ * @param mixed $val value
+ * @param int|true $server server
+ *
+ * @return mixed
+ */
+ public function cacheSet($var, $val = null, $server = 0)
+ {
+ if ($server === true) {
+ $server = $GLOBALS['server'];
+ }
+ $_SESSION['cache']['server_' . $server][$var] = $val;
+ }
+
+
+ /**
+ * Removes cached information from the session
+ *
+ * @param string $var variable name
+ * @param int|true $server server
+ *
+ * @return void
+ */
+ public function cacheUnset($var, $server = 0)
+ {
+ if ($server === true) {
+ $server = $GLOBALS['server'];
+ }
+ unset($_SESSION['cache']['server_' . $server][$var]);
+ }
+
+
+ /**
+ * Converts a bit value to printable format;
+ * in MySQL a BIT field can be from 1 to 64 bits so we need this
+ * function because in PHP, decbin() supports only 32 bits
+ *
+ * @param numeric $value coming from a BIT field
+ * @param integer $length length
+ *
+ * @return string the printable value
+ */
+ public function printableBitValue($value, $length)
+ {
+ $printable = '';
+ for ($i = 0, $len_ceiled = ceil($length / 8); $i < $len_ceiled; $i++) {
+ $printable .= sprintf('%08d', decbin(ord(substr($value, $i, 1))));
+ }
+ $printable = substr($printable, -$length);
+ return $printable;
+ }
+
+
+ /**
+ * Verifies whether the value contains a non-printable character
+ *
+ * @param string $value value
+ *
+ * @return boolean
+ */
+ public function containsNonPrintableAscii($value)
+ {
+ return preg_match('@[^[:print:]]@', $value);
+ }
+
+
+ /**
+ * Converts a BIT type default value
+ * for example, b'010' becomes 010
+ *
+ * @param string $bit_default_value value
+ *
+ * @return string the converted value
+ */
+ public function convertBitDefaultValue($bit_default_value)
+ {
+ return strtr($bit_default_value, array("b" => "", "'" => ""));
+ }
+
+
+ /**
+ * Extracts the various parts from a column spec
+ *
+ * @param string $columnspec Column specification
+ *
+ * @return array associative array containing type, spec_in_brackets
+ * and possibly enum_set_values (another array)
+ */
+ public function extractColumnSpec($columnspec)
+ {
+ $first_bracket_pos = strpos($columnspec, '(');
+ if ($first_bracket_pos) {
+ $spec_in_brackets = chop(
+ substr(
+ $columnspec,
+ $first_bracket_pos + 1,
+ (strrpos($columnspec, ')') - $first_bracket_pos - 1)
+ )
+ );
+ // convert to lowercase just to be sure
+ $type = strtolower(chop(substr($columnspec, 0, $first_bracket_pos)));
+ } else {
+ $type = strtolower($columnspec);
+ $spec_in_brackets = '';
+ }
+
+ if ('enum' == $type || 'set' == $type) {
+
+ // Define our working vars
+ $enum_set_values = array();
+ $working = "";
+ $in_string = false;
+ $index = 0;
+
+ // While there is another character to process
+ while (isset($columnspec[$index])) {
+
+ // Grab the char to look at
+ $char = $columnspec[$index];
+
+ // If it is a single quote, needs to be handled specially
+ if ($char == "'") {
+
+ // If we are not currently in a string, begin one
+ if (! $in_string) {
+ $in_string = true;
+ $working = "";
+ } else {
+ // Otherwise, it may be either an end of a string,
+ // or a 'double quote' which can be handled as-is
+ // Check out the next character (if possible)
+ $has_next = isset($columnspec[$index + 1]);
+ $next = $has_next ? $columnspec[$index + 1] : null;
+
+ // If we have reached the end of our 'working' string
+ // (because there are no more chars,or the next char
+ // is not another quote)
+ if (! $has_next || $next != "'") {
+ $enum_set_values[] = $working;
+ $in_string = false;
+
+ } elseif ($next == "'") {
+ // Otherwise, this is a 'double quote',
+ // and can be added to the working string
+ $working .= "'";
+ // Skip the next char; we already know what it is
+ $index++;
+ }
+ }
+
+ } elseif ('\\' == $char
+ && isset($columnspec[$index + 1])
+ && "'" == $columnspec[$index + 1]
+ ) {
+ // escaping of a quote?
+ $working .= "'";
+ $index++;
+ } else {
+ // Otherwise, add it to our working string like normal
+ $working .= $char;
+ }
+
+ // Increment character index
+ $index++;
+ } // end while
+
+ $printtype = $type
+ . '(' . str_replace("','", "', '", $spec_in_brackets) . ')';
+ $binary = false;
+ $unsigned = false;
+ $zerofill = false;
+
+ } else {
+
+ $enum_set_values = array();
+
+ /* Create printable type name */
+ $printtype = strtolower($columnspec);
+
+ // Strip the "BINARY" attribute, except if we find "BINARY(" because
+ // this would be a BINARY or VARBINARY column type;
+ // by the way, a BLOB should not show the BINARY attribute
+ // because this is not accepted in MySQL syntax.
+ if (preg_match('@binary@', $printtype)
+ && ! preg_match('@binary[\(]@', $printtype)
+ ) {
+ $printtype = preg_replace('@binary@', '', $printtype);
+ $binary = true;
+ } else {
+ $binary = false;
+ }
+
+ $printtype = preg_replace(
+ '@zerofill@', '', $printtype, -1, $zerofill_cnt
+ );
+ $zerofill = ($zerofill_cnt > 0);
+ $printtype = preg_replace(
+ '@unsigned@', '', $printtype, -1, $unsigned_cnt
+ );
+ $unsigned = ($unsigned_cnt > 0);
+ $printtype = trim($printtype);
+
+ }
+
+ $attribute = ' ';
+ if ($binary) {
+ $attribute = 'BINARY';
+ }
+ if ($unsigned) {
+ $attribute = 'UNSIGNED';
+ }
+ if ($zerofill) {
+ $attribute = 'UNSIGNED ZEROFILL';
+ }
+
+ $can_contain_collation = false;
+ if (! $binary
+ && preg_match(
+ "@^(char|varchar|text|tinytext|mediumtext|longtext|set|enum)@", $type
+ )
+ ) {
+ $can_contain_collation = true;
+ }
+
+ // for the case ENUM('–','“')
+ $displayed_type = htmlspecialchars($printtype);
+ if (strlen($printtype) > $GLOBALS['cfg']['LimitChars']) {
+ $displayed_type = '
';
+ $displayed_type .= substr($printtype, 0, $GLOBALS['cfg']['LimitChars']);
+ $displayed_type .= ' ';
+ }
+
+ return array(
+ 'type' => $type,
+ 'spec_in_brackets' => $spec_in_brackets,
+ 'enum_set_values' => $enum_set_values,
+ 'print_type' => $printtype,
+ 'binary' => $binary,
+ 'unsigned' => $unsigned,
+ 'zerofill' => $zerofill,
+ 'attribute' => $attribute,
+ 'can_contain_collation' => $can_contain_collation,
+ 'displayed_type' => $displayed_type
+ );
+ }
+
+
+ /**
+ * Verifies if this table's engine supports foreign keys
+ *
+ * @param string $engine engine
+ *
+ * @return boolean
+ */
+ public function isForeignKeySupported($engine)
+ {
+ $engine = strtoupper($engine);
+ if (($engine == 'INNODB') || ($engine == 'PBXT')) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Replaces some characters by a displayable equivalent
+ *
+ * @param string $content content
+ *
+ * @return string the content with characters replaced
+ */
+ public function replaceBinaryContents($content)
+ {
+ $result = str_replace("\x00", '\0', $content);
+ $result = str_replace("\x08", '\b', $result);
+ $result = str_replace("\x0a", '\n', $result);
+ $result = str_replace("\x0d", '\r', $result);
+ $result = str_replace("\x1a", '\Z', $result);
+ return $result;
+ }
+
+
+ /**
+ * Converts GIS data to Well Known Text format
+ *
+ * @param binary $data GIS data
+ * @param bool $includeSRID Add SRID to the WKT
+ *
+ * @return string GIS data in Well Know Text format
+ */
+ public function asWKT($data, $includeSRID = false)
+ {
+ // Convert to WKT format
+ $hex = bin2hex($data);
+ $wktsql = "SELECT ASTEXT(x'" . $hex . "')";
+ if ($includeSRID) {
+ $wktsql .= ", SRID(x'" . $hex . "')";
+ }
+
+ $wktresult = PMA_DBI_try_query($wktsql, null, PMA_DBI_QUERY_STORE);
+ $wktarr = PMA_DBI_fetch_row($wktresult, 0);
+ $wktval = $wktarr[0];
+
+ if ($includeSRID) {
+ $srid = $wktarr[1];
+ $wktval = "'" . $wktval . "'," . $srid;
+ }
+ @PMA_DBI_free_result($wktresult);
+
+ return $wktval;
+ }
+
+
+ /**
+ * If the string starts with a \r\n pair (0x0d0a) add an extra \n
+ *
+ * @param string $string string
+ *
+ * @return string with the chars replaced
+ */
+ public function duplicateFirstNewline($string)
+ {
+ $first_occurence = strpos($string, "\r\n");
+ if ($first_occurence === 0) {
+ $string = "\n" . $string;
+ }
+ return $string;
+ }
+
+
+ /**
+ * Get the action word corresponding to a script name
+ * in order to display it as a title in navigation panel
+ *
+ * @param string $target a valid value for $cfg['LeftDefaultTabTable'],
+ * $cfg['DefaultTabTable'] or $cfg['DefaultTabDatabase']
+ *
+ * @return array
+ */
+ public function getTitleForTarget($target)
+ {
+ $mapping = array(
+ // Values for $cfg['DefaultTabTable']
+ 'tbl_structure.php' => __('Structure'),
+ 'tbl_sql.php' => __('SQL'),
+ 'tbl_select.php' =>__('Search'),
+ 'tbl_change.php' =>__('Insert'),
+ 'sql.php' => __('Browse'),
+
+ // Values for $cfg['DefaultTabDatabase']
+ 'db_structure.php' => __('Structure'),
+ 'db_sql.php' => __('SQL'),
+ 'db_search.php' => __('Search'),
+ 'db_operations.php' => __('Operations'),
+ );
+ return $mapping[$target];
+ }
+
+
+ /**
+ * Formats user string, expanding @VARIABLES@, accepting strftime format
+ * string.
+ *
+ * @param string $string Text where to do expansion.
+ * @param function $escape Function to call for escaping variable values.
+ * @param array $updates Array with overrides for default parameters
+ * (obtained from GLOBALS).
+ *
+ * @return string
+ */
+ public function expandUserString($string, $escape = null, $updates = array())
+ {
+ /* Content */
+ $vars['http_host'] = PMA_getenv('HTTP_HOST');
+ $vars['server_name'] = $GLOBALS['cfg']['Server']['host'];
+ $vars['server_verbose'] = $GLOBALS['cfg']['Server']['verbose'];
+
+ if (empty($GLOBALS['cfg']['Server']['verbose'])) {
+ $vars['server_verbose_or_name'] = $GLOBALS['cfg']['Server']['host'];
+ } else {
+ $vars['server_verbose_or_name'] = $GLOBALS['cfg']['Server']['verbose'];
+ }
+
+ $vars['database'] = $GLOBALS['db'];
+ $vars['table'] = $GLOBALS['table'];
+ $vars['phpmyadmin_version'] = 'phpMyAdmin ' . PMA_VERSION;
+
+ /* Update forced variables */
+ foreach ($updates as $key => $val) {
+ $vars[$key] = $val;
+ }
+
+ /* Replacement mapping */
+ /*
+ * The __VAR__ ones are for backward compatibility, because user
+ * might still have it in cookies.
+ */
+ $replace = array(
+ '@HTTP_HOST@' => $vars['http_host'],
+ '@SERVER@' => $vars['server_name'],
+ '__SERVER__' => $vars['server_name'],
+ '@VERBOSE@' => $vars['server_verbose'],
+ '@VSERVER@' => $vars['server_verbose_or_name'],
+ '@DATABASE@' => $vars['database'],
+ '__DB__' => $vars['database'],
+ '@TABLE@' => $vars['table'],
+ '__TABLE__' => $vars['table'],
+ '@PHPMYADMIN@' => $vars['phpmyadmin_version'],
+ );
+
+ /* Optional escaping */
+ if (! is_null($escape)) {
+ foreach ($replace as $key => $val) {
+ $replace[$key] = ($escape == 'backquote')
+ ? $this->$escape($val)
+ : $escape($val);
+ }
+ }
+
+ /* Backward compatibility in 3.5.x */
+ if (strpos($string, '@FIELDS@') !== false) {
+ $string = strtr($string, array('@FIELDS@' => '@COLUMNS@'));
+ }
+
+ /* Fetch columns list if required */
+ if (strpos($string, '@COLUMNS@') !== false) {
+ $columns_list = PMA_DBI_get_columns($GLOBALS['db'], $GLOBALS['table']);
+
+ $column_names = array();
+ foreach ($columns_list as $column) {
+ if (! is_null($escape)) {
+ $column_names[] = $escape($column['Field']);
+ } else {
+ $column_names[] = $column['Field'];
+ }
+ }
+
+ $replace['@COLUMNS@'] = implode(',', $column_names);
+ }
+
+ /* Do the replacement */
+ return strtr(strftime($string), $replace);
+ }
+
+
+ /**
+ * Prepare the form used to browse anywhere on the local server for a file to
+ * import
+ *
+ * @param string $max_upload_size maximum upload size
+ *
+ * @return void
+ */
+ public function getBrowseUploadFileBlock($max_upload_size)
+ {
+
+ $block_html = '';
+
+ if ($GLOBALS['is_upload'] && ! empty($GLOBALS['cfg']['UploadDir'])) {
+ $block_html .= '
';
+ } else {
+ $block_html .= '';
+ }
+
+ $block_html .= __("Browse your computer:") . ' '
+ . '
'
+ . '
'
+ . ' '
+ . $this->getFormattedMaximumUploadSize($max_upload_size) . "\n"
+ // some browsers should respect this :)
+ . $this->generateHiddenMaxFileSize($max_upload_size) . "\n";
+
+ return $block_html;
+ }
+
+
+ /**
+ * Prepare the form used to select a file to import from the server upload
+ * directory
+ *
+ * @param array $import_list array of import plugins
+ * @param string $uploaddir upload directory
+ *
+ * @return void
+ */
+ public function getSelectUploadFileBlock($import_list, $uploaddir)
+ {
+
+ $block_html = '';
+
+ $block_html .= ''
+ . sprintf(
+ __("Select from the web server upload directory %s :"),
+ htmlspecialchars($this->userDir($uploaddir))
+ )
+ . ' ';
+
+ $extensions = '';
+ foreach ($import_list as $import_plugin) {
+ if (! empty($extensions)) {
+ $extensions .= '|';
+ }
+ $properties = $import_plugin->getProperties();
+ $extensions .= $properties['extension'];
+ }
+
+ $matcher = '@\.(' . $extensions . ')(\.('
+ . PMA_supportedDecompressions() . '))?$@';
+
+ $active = (isset($GLOBALS['timeout_passed']) && $GLOBALS['timeout_passed']
+ && isset($local_import_file))
+ ? $local_import_file
+ : '';
+
+ $files = PMA_getFileSelectOptions(
+ $this->userDir($uploaddir),
+ $matcher,
+ $active
+ );
+
+ if ($files === false) {
+ PMA_Message::error(
+ __('The directory you set for upload work cannot be reached')
+ )->display();
+ } elseif (! empty($files)) {
+ $block_html .= "\n"
+ . ' ' . "\n"
+ . ' ' . "\n"
+ . $files
+ . ' ' . "\n";
+ } elseif (empty ($files)) {
+ $block_html .= '' . __('There are no files to upload') . ' ';
+ }
+
+ return $block_html;
+
+ }
+
+
+ /**
+ * Build titles and icons for action links
+ *
+ * @return array the action titles
+ */
+ public function buildActionTitles()
+ {
+ $titles = array();
+
+ $titles['Browse'] = $this->getIcon('b_browse.png', __('Browse'));
+ $titles['NoBrowse'] = $this->getIcon('bd_browse.png', __('Browse'));
+ $titles['Search'] = $this->getIcon('b_select.png', __('Search'));
+ $titles['NoSearch'] = $this->getIcon('bd_select.png', __('Search'));
+ $titles['Insert'] = $this->getIcon('b_insrow.png', __('Insert'));
+ $titles['NoInsert'] = $this->getIcon('bd_insrow.png', __('Insert'));
+ $titles['Structure'] = $this->getIcon('b_props.png', __('Structure'));
+ $titles['Drop'] = $this->getIcon('b_drop.png', __('Drop'));
+ $titles['NoDrop'] = $this->getIcon('bd_drop.png', __('Drop'));
+ $titles['Empty'] = $this->getIcon('b_empty.png', __('Empty'));
+ $titles['NoEmpty'] = $this->getIcon('bd_empty.png', __('Empty'));
+ $titles['Edit'] = $this->getIcon('b_edit.png', __('Edit'));
+ $titles['NoEdit'] = $this->getIcon('bd_edit.png', __('Edit'));
+ $titles['Export'] = $this->getIcon('b_export.png', __('Export'));
+ $titles['NoExport'] = $this->getIcon('bd_export.png', __('Export'));
+ $titles['Execute'] = $this->getIcon('b_nextpage.png', __('Execute'));
+ $titles['NoExecute'] = $this->getIcon('bd_nextpage.png', __('Execute'));
+
+ return $titles;
+ }
+
+
+ /**
+ * This function processes the datatypes supported by the DB,
+ * as specified in PMA_Types->getColumns() and either returns an array
+ * (useful for quickly checking if a datatype is supported)
+ * or an HTML snippet that creates a drop-down list.
+ *
+ * @param bool $html Whether to generate an html snippet or an array
+ * @param string $selected The value to mark as selected in HTML mode
+ *
+ * @return mixed An HTML snippet or an array of datatypes.
+ *
+ */
+ public function getSupportedDatatypes($html = false, $selected = '')
+ {
+ if ($html) {
+
+ // NOTE: the SELECT tag in not included in this snippet.
+ $retval = '';
+
+ foreach ($GLOBALS['PMA_Types']->getColumns() as $key => $value) {
+
+ if (is_array($value)) {
+
+ $retval .= "";
+ foreach ($value as $subvalue) {
+
+ if ($subvalue == $selected) {
+ $retval .= sprintf(
+ '%s ',
+ $GLOBALS['PMA_Types']->getTypeDescription($subvalue),
+ $subvalue
+ );
+ } else if ($subvalue === '-') {
+ $retval .= '';
+ $retval .= $subvalue;
+ $retval .= ' ';
+ } else {
+ $retval .= sprintf(
+ '%s ',
+ $GLOBALS['PMA_Types']->getTypeDescription($subvalue),
+ $subvalue
+ );
+ }
+ }
+ $retval .= ' ';
+
+ } else {
+
+ if ($selected == $value) {
+ $retval .= sprintf(
+ '%s ',
+ $GLOBALS['PMA_Types']->getTypeDescription($value),
+ $value
+ );
+ } else {
+ $retval .= sprintf(
+ '%s ',
+ $GLOBALS['PMA_Types']->getTypeDescription($value),
+ $value
+ );
+ }
+
+ }
+ }
+
+ } else {
+ $retval = array();
+ foreach ($GLOBALS['PMA_Types']->getColumns() as $value) {
+
+ if (is_array($value)) {
+ foreach ($value as $subvalue) {
+ if ($subvalue !== '-') {
+ $retval[] = $subvalue;
+ }
+ }
+ } else {
+ if ($value !== '-') {
+ $retval[] = $value;
+ }
+ }
+
+ }
+ }
+
+ return $retval;
+ } // end getSupportedDatatypes()
+
+
+ /**
+ * Returns a list of datatypes that are not (yet) handled by PMA.
+ * Used by: tbl_change.php and libraries/db_routines.inc.php
+ *
+ * @return array list of datatypes
+ */
+ public function unsupportedDatatypes()
+ {
+ $no_support_types = array();
+ return $no_support_types;
+ }
+
+
+ /**
+ * Return GIS data types
+ *
+ * @param bool $upper_case whether to return values in upper case
+ *
+ * @return array GIS data types
+ */
+ public function getGISDatatypes($upper_case = false)
+ {
+ $gis_data_types = array(
+ 'geometry',
+ 'point',
+ 'linestring',
+ 'polygon',
+ 'multipoint',
+ 'multilinestring',
+ 'multipolygon',
+ 'geometrycollection'
+ );
+ if ($upper_case) {
+ for ($i = 0; $i < count($gis_data_types); $i++) {
+ $gis_data_types[$i] = strtoupper($gis_data_types[$i]);
+ }
+ }
+ return $gis_data_types;
+ }
+
+
+ /**
+ * Generates GIS data based on the string passed.
+ *
+ * @param string $gis_string GIS string
+ *
+ * @return string GIS data enclosed in 'GeomFromText' function
+ */
+ public function createGISData($gis_string)
+ {
+ $gis_string = trim($gis_string);
+ $geom_types = '(POINT|MULTIPOINT|LINESTRING|MULTILINESTRING|'
+ . 'POLYGON|MULTIPOLYGON|GEOMETRYCOLLECTION)';
+ if (preg_match("/^'" . $geom_types . "\(.*\)',[0-9]*$/i", $gis_string)) {
+ return 'GeomFromText(' . $gis_string . ')';
+ } elseif (preg_match("/^" . $geom_types . "\(.*\)$/i", $gis_string)) {
+ return "GeomFromText('" . $gis_string . "')";
+ } else {
+ return $gis_string;
+ }
+ }
+
+
+ /**
+ * Returns the names and details of the functions
+ * that can be applied on geometry data typess.
+ *
+ * @param string $geom_type if provided the output is limited to the functions
+ * that are applicable to the provided geometry type.
+ * @param bool $binary if set to false functions that take two geometries
+ * as arguments will not be included.
+ * @param bool $display if set to true separators will be added to the
+ * output array.
+ *
+ * @return array names and details of the functions that can be applied on
+ * geometry data typess.
+ */
+ public function getGISFunctions(
+ $geom_type = null, $binary = true, $display = false
+ ) {
+ $funcs = array();
+ if ($display) {
+ $funcs[] = array('display' => ' ');
+ }
+
+ // Unary functions common to all geomety types
+ $funcs['Dimension'] = array('params' => 1, 'type' => 'int');
+ $funcs['Envelope'] = array('params' => 1, 'type' => 'Polygon');
+ $funcs['GeometryType'] = array('params' => 1, 'type' => 'text');
+ $funcs['SRID'] = array('params' => 1, 'type' => 'int');
+ $funcs['IsEmpty'] = array('params' => 1, 'type' => 'int');
+ $funcs['IsSimple'] = array('params' => 1, 'type' => 'int');
+
+ $geom_type = trim(strtolower($geom_type));
+ if ($display && $geom_type != 'geometry' && $geom_type != 'multipoint') {
+ $funcs[] = array('display' => '--------');
+ }
+
+ // Unary functions that are specific to each geomety type
+ if ($geom_type == 'point') {
+ $funcs['X'] = array('params' => 1, 'type' => 'float');
+ $funcs['Y'] = array('params' => 1, 'type' => 'float');
+
+ } elseif ($geom_type == 'multipoint') {
+ // no fucntions here
+ } elseif ($geom_type == 'linestring') {
+ $funcs['EndPoint'] = array('params' => 1, 'type' => 'point');
+ $funcs['GLength'] = array('params' => 1, 'type' => 'float');
+ $funcs['NumPoints'] = array('params' => 1, 'type' => 'int');
+ $funcs['StartPoint'] = array('params' => 1, 'type' => 'point');
+ $funcs['IsRing'] = array('params' => 1, 'type' => 'int');
+
+ } elseif ($geom_type == 'multilinestring') {
+ $funcs['GLength'] = array('params' => 1, 'type' => 'float');
+ $funcs['IsClosed'] = array('params' => 1, 'type' => 'int');
+
+ } elseif ($geom_type == 'polygon') {
+ $funcs['Area'] = array('params' => 1, 'type' => 'float');
+ $funcs['ExteriorRing'] = array('params' => 1, 'type' => 'linestring');
+ $funcs['NumInteriorRings'] = array('params' => 1, 'type' => 'int');
+
+ } elseif ($geom_type == 'multipolygon') {
+ $funcs['Area'] = array('params' => 1, 'type' => 'float');
+ $funcs['Centroid'] = array('params' => 1, 'type' => 'point');
+ // Not yet implemented in MySQL
+ //$funcs['PointOnSurface'] = array('params' => 1, 'type' => 'point');
+
+ } elseif ($geom_type == 'geometrycollection') {
+ $funcs['NumGeometries'] = array('params' => 1, 'type' => 'int');
+ }
+
+ // If we are asked for binary functions as well
+ if ($binary) {
+ // section separator
+ if ($display) {
+ $funcs[] = array('display' => '--------');
+ }
+
+ if (PMA_MYSQL_INT_VERSION < 50601) {
+ $funcs['Crosses'] = array('params' => 2, 'type' => 'int');
+ $funcs['Contains'] = array('params' => 2, 'type' => 'int');
+ $funcs['Disjoint'] = array('params' => 2, 'type' => 'int');
+ $funcs['Equals'] = array('params' => 2, 'type' => 'int');
+ $funcs['Intersects'] = array('params' => 2, 'type' => 'int');
+ $funcs['Overlaps'] = array('params' => 2, 'type' => 'int');
+ $funcs['Touches'] = array('params' => 2, 'type' => 'int');
+ $funcs['Within'] = array('params' => 2, 'type' => 'int');
+ } else {
+ // If MySQl version is greaeter than or equal 5.6.1,
+ // use the ST_ prefix.
+ $funcs['ST_Crosses'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Contains'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Disjoint'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Equals'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Intersects'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Overlaps'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Touches'] = array('params' => 2, 'type' => 'int');
+ $funcs['ST_Within'] = array('params' => 2, 'type' => 'int');
+ }
+
+ if ($display) {
+ $funcs[] = array('display' => '--------');
+ }
+ // Minimum bounding rectangle functions
+ $funcs['MBRContains'] = array('params' => 2, 'type' => 'int');
+ $funcs['MBRDisjoint'] = array('params' => 2, 'type' => 'int');
+ $funcs['MBREquals'] = array('params' => 2, 'type' => 'int');
+ $funcs['MBRIntersects'] = array('params' => 2, 'type' => 'int');
+ $funcs['MBROverlaps'] = array('params' => 2, 'type' => 'int');
+ $funcs['MBRTouches'] = array('params' => 2, 'type' => 'int');
+ $funcs['MBRWithin'] = array('params' => 2, 'type' => 'int');
+ }
+ return $funcs;
+ }
+
+
+ /**
+ * Returns default function for a particular column.
+ *
+ * @param array $field Data about the column for which
+ * to generate the dropdown
+ * @param bool $insert_mode Whether the operation is 'insert'
+ *
+ * @global array $cfg PMA configuration
+ * @global array $analyzed_sql Analyzed SQL query
+ * @global mixed $data data of currently edited row
+ * (used to detect whether to choose defaults)
+ *
+ * @return string An HTML snippet of a dropdown list with function
+ * names appropriate for the requested column.
+ */
+ public function getDefaultFunctionForField($field, $insert_mode)
+ {
+ global $cfg, $analyzed_sql, $data;
+
+ $default_function = '';
+
+ // Can we get field class based values?
+ $current_class = $GLOBALS['PMA_Types']->getTypeClass($field['True_Type']);
+ if (! empty($current_class)) {
+ if (isset($cfg['DefaultFunctions']['FUNC_' . $current_class])) {
+ $default_function
+ = $cfg['DefaultFunctions']['FUNC_' . $current_class];
+ }
+ }
+
+ $analyzed_sql_field_array = $analyzed_sql[0]['create_table_fields']
+ [$field['Field']];
+ // what function defined as default?
+ // for the first timestamp we don't set the default function
+ // if there is a default value for the timestamp
+ // (not including CURRENT_TIMESTAMP)
+ // and the column does not have the
+ // ON UPDATE DEFAULT TIMESTAMP attribute.
+ if (($field['True_Type'] == 'timestamp')
+ && empty($field['Default'])
+ && empty($data)
+ && ! isset($analyzed_sql_field_array['on_update_current_timestamp'])
+ && ($analyzed_sql_field_array['default_value'] != 'NULL')
+ ) {
+ $default_function = $cfg['DefaultFunctions']['first_timestamp'];
+ }
+
+ // Default for first timestamp field
+ if ($field['first_timestamp']) {
+ $default_function = $cfg['DefaultFunctions']['first_timestamp'];
+ }
+
+ // For primary keys of type char(36) or varchar(36) UUID if the default
+ // function
+ // Only applies to insert mode, as it would silently trash data on updates.
+ if ($insert_mode
+ && $field['Key'] == 'PRI'
+ && ($field['Type'] == 'char(36)' || $field['Type'] == 'varchar(36)')
+ ) {
+ $default_function = $cfg['DefaultFunctions']['FUNC_UUID'];
+ }
+
+ // this is set only when appropriate and is always true
+ if (isset($field['display_binary_as_hex'])) {
+ $default_function = 'UNHEX';
+ }
+
+ return $default_function;
+ }
+
+
+ /**
+ * Creates a dropdown box with MySQL functions for a particular column.
+ *
+ * @param array $field Data about the column for which
+ * to generate the dropdown
+ * @param bool $insert_mode Whether the operation is 'insert'
+ *
+ * @return string An HTML snippet of a dropdown list with function
+ * names appropriate for the requested column.
+ */
+ public function getFunctionsForField($field, $insert_mode)
+ {
+ $default_function = $this->getDefaultFunctionForField($field, $insert_mode);
+ $dropdown_built = array();
+
+ // Create the output
+ $retval = ' ' . "\n";
+ // loop on the dropdown array and print all available options for that
+ // field.
+ $functions = $GLOBALS['PMA_Types']->getFunctions($field['True_Type']);
+ foreach ($functions as $function) {
+ $retval .= '' . "\n";
+ $dropdown_built[$function] = true;
+ }
+
+ // Create separator before all functions list
+ if (count($functions) > 0) {
+ $retval .= ' -------- '
+ . "\n";
+ }
+
+ // For compatibility's sake, do not let out all other functions. Instead
+ // print a separator (blank) and then show ALL functions which weren't
+ // shown yet.
+ $functions = $GLOBALS['PMA_Types']->getAllFunctions();
+ foreach ($functions as $function) {
+ // Skip already included functions
+ if (isset($dropdown_built[$function])) {
+ continue;
+ }
+ $retval .= '' . "\n";
+ } // end for
+
+ return $retval;
+ } // end getFunctionsForField()
+
+
+ /**
+ * Checks if the current user has a specific privilege and returns true if the
+ * user indeed has that privilege or false if (s)he doesn't. This function must
+ * only be used for features that are available since MySQL 5, because it
+ * relies on the INFORMATION_SCHEMA database to be present.
+ *
+ * Example: currentUserHasPrivilege('CREATE ROUTINE', 'mydb');
+ * // Checks if the currently logged in user has the global
+ * // 'CREATE ROUTINE' privilege or, if not, checks if the
+ * // user has this privilege on database 'mydb'.
+ *
+ * @param string $priv The privilege to check
+ * @param mixed $db null, to only check global privileges
+ * string, db name where to also check for privileges
+ * @param mixed $tbl null, to only check global/db privileges
+ * string, table name where to also check for privileges
+ *
+ * @return bool
+ */
+ public function currentUserHasPrivilege($priv, $db = null, $tbl = null)
+ {
+ // Get the username for the current user in the format
+ // required to use in the information schema database.
+ $user = PMA_DBI_fetch_value("SELECT CURRENT_USER();");
+ if ($user === false) {
+ return false;
+ }
+
+ $user = explode('@', $user);
+ $username = "''";
+ $username .= str_replace("'", "''", $user[0]);
+ $username .= "''@''";
+ $username .= str_replace("'", "''", $user[1]);
+ $username .= "''";
+
+ // Prepage the query
+ $query = "SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`%s` "
+ . "WHERE GRANTEE='%s' AND PRIVILEGE_TYPE='%s'";
+
+ // Check global privileges first.
+ $user_privileges = PMA_DBI_fetch_value(
+ sprintf(
+ $query,
+ 'USER_PRIVILEGES',
+ $username,
+ $priv
+ )
+ );
+ if ($user_privileges) {
+ return true;
+ }
+ // If a database name was provided and user does not have the
+ // required global privilege, try database-wise permissions.
+ if ($db !== null) {
+ // need to escape wildcards in db and table names, see bug #3518484
+ $db = str_replace(array('%', '_'), array('\%', '\_'), $db);
+ $query .= " AND TABLE_SCHEMA='%s'";
+ $schema_privileges = PMA_DBI_fetch_value(
+ sprintf(
+ $query,
+ 'SCHEMA_PRIVILEGES',
+ $username,
+ $priv,
+ $this->sqlAddSlashes($db)
+ )
+ );
+ if ($schema_privileges) {
+ return true;
+ }
+ } else {
+ // There was no database name provided and the user
+ // does not have the correct global privilege.
+ return false;
+ }
+ // If a table name was also provided and we still didn't
+ // find any valid privileges, try table-wise privileges.
+ if ($tbl !== null) {
+ // need to escape wildcards in db and table names, see bug #3518484
+ $tbl = str_replace(array('%', '_'), array('\%', '\_'), $tbl);
+ $query .= " AND TABLE_NAME='%s'";
+ $table_privileges = PMA_DBI_fetch_value(
+ sprintf(
+ $query,
+ 'TABLE_PRIVILEGES',
+ $username,
+ $priv,
+ $this->sqlAddSlashes($db),
+ $this->sqlAddSlashes($tbl)
+ )
+ );
+ if ($table_privileges) {
+ return true;
+ }
+ }
+ // If we reached this point, the user does not
+ // have even valid table-wise privileges.
+ return false;
+ }
+
+
+ /**
+ * Returns server type for current connection
+ *
+ * Known types are: Drizzle, MariaDB and MySQL (default)
+ *
+ * @return string
+ */
+ public function getServerType()
+ {
+ $server_type = 'MySQL';
+ if (PMA_DRIZZLE) {
+ $server_type = 'Drizzle';
+ } else if (strpos(PMA_MYSQL_STR_VERSION, 'mariadb') !== false) {
+ $server_type = 'MariaDB';
+ } else if (stripos(PMA_MYSQL_VERSION_COMMENT, 'percona') !== false) {
+ $server_type = 'Percona Server';
+ }
+ return $server_type;
+ }
+
+
+ /**
+ * Analyzes the limit clause and return the start and length attributes of it.
+ *
+ * @param string $limit_clause limit clause
+ *
+ * @return array Start and length attributes of the limit clause
+ */
+ public function analyzeLimitClause($limit_clause)
+ {
+ $start_and_length = explode(',', str_ireplace('LIMIT', '', $limit_clause));
+ return array(
+ 'start' => trim($start_and_length[0]),
+ 'length' => trim($start_and_length[1])
+ );
+ }
+
+
+ /**
+ * Prepare HTML code for display button.
+ *
+ * @return void
+ */
+ public function getButton()
+ {
+ return ''
+ . ' '
+ . '
';
+ }
+
+
+ /**
+ * Parses ENUM/SET values
+ *
+ * @param string $definition The definition of the column
+ * for which to parse the values
+ *
+ * @return array
+ */
+ public function parseEnumSetValues($definition)
+ {
+ $values_string = htmlentities($definition);
+ // There is a JS port of the below parser in functions.js
+ // If you are fixing something here,
+ // you need to also update the JS port.
+ $values = array();
+ $in_string = false;
+ $buffer = '';
+
+ for ($i=0; $i 0) {
+ // The leftovers in the buffer are the last value (if any)
+ $values[] = $buffer;
+ }
+
+ return $values;
+ }
+
+}
+
+?>
diff --git a/libraries/Config.class.php b/libraries/Config.class.php
index cad6eff80b..01d47aa670 100644
--- a/libraries/Config.class.php
+++ b/libraries/Config.class.php
@@ -12,7 +12,7 @@ if (! defined('PHPMYADMIN')) {
/**
* Load vendor configuration.
*/
-require './libraries/vendor_config.php';
+require_once './libraries/vendor_config.php';
/**
* Configuration class
diff --git a/libraries/DbSearch.class.php b/libraries/DbSearch.class.php
new file mode 100644
index 0000000000..7c42982bbb
--- /dev/null
+++ b/libraries/DbSearch.class.php
@@ -0,0 +1,513 @@
+_db = $db;
+ // Sets criteria parameters
+ $this->_setSearchParams();
+ }
+
+ /**
+ * Get CommmonFunctions
+ *
+ * @return CommonFunctions object
+ */
+ public function getCommonFunctions()
+ {
+ if (is_null($this->_common_functions)) {
+ $this->_common_functions = PMA_CommonFunctions::getInstance();
+ }
+ return $this->_common_functions;
+ }
+
+ /**
+ * Sets search parameters
+ *
+ */
+ private function _setSearchParams()
+ {
+ $this->_tables_names_only = PMA_DBI_get_tables($this->_db);
+
+ $this->_searchTypes = array(
+ '1' => __('at least one of the words'),
+ '2' => __('all words'),
+ '3' => __('the exact phrase'),
+ '4' => __('as regular expression'),
+ );
+
+ if (empty($_REQUEST['criteriaSearchType'])
+ || ! is_string($_REQUEST['criteriaSearchType'])
+ || ! array_key_exists($_REQUEST['criteriaSearchType'], $this->_searchTypes)
+ ) {
+ $this->_criteriaSearchType = 1;
+ unset($_REQUEST['submit_search']);
+ } else {
+ $this->_criteriaSearchType = (int) $_REQUEST['criteriaSearchType'];
+ $this->_searchTypeDescription = $this->_searchTypes[$_REQUEST['criteriaSearchType']];
+ }
+
+ if (empty($_REQUEST['criteriaSearchString'])
+ || ! is_string($_REQUEST['criteriaSearchString'])
+ ) {
+ $this->_criteriaSearchString = '';
+ unset($_REQUEST['submit_search']);
+ } else {
+ $this->_criteriaSearchString = $_REQUEST['criteriaSearchString'];
+ }
+
+ $this->_criteriaTables = array();
+ if (empty($_REQUEST['criteriaTables']) || ! is_array($_REQUEST['criteriaTables'])) {
+ unset($_REQUEST['submit_search']);
+ } elseif (! isset($_REQUEST['selectall']) && ! isset($_REQUEST['unselectall'])) {
+ $this->_criteriaTables = array_intersect(
+ $_REQUEST['criteriaTables'], $this->_tables_names_only
+ );
+ }
+
+ if (isset($_REQUEST['selectall'])) {
+ $this->_criteriaTables = $this->_tables_names_only;
+ } elseif (isset($_REQUEST['unselectall'])) {
+ $this->_criteriaTables = array();
+ }
+
+ if (empty($_REQUEST['criteriaColumnName'])
+ || ! is_string($_REQUEST['criteriaColumnName'])
+ ) {
+ unset($this->_criteriaColumnName);
+ } else {
+ $this->_criteriaColumnName = $this->getCommonFunctions()->sqlAddSlashes(
+ $_REQUEST['criteriaColumnName'], true
+ );
+ }
+ }
+
+ /**
+ * Builds the SQL search query
+ *
+ * @param string $table The table name
+ *
+ * @return array 3 SQL querys (for count, display and delete results)
+ *
+ * @todo can we make use of fulltextsearch IN BOOLEAN MODE for this?
+ * PMA_backquote
+ * PMA_DBI_free_result
+ * PMA_DBI_fetch_assoc
+ * $GLOBALS['db']
+ * explode
+ * count
+ * strlen
+ */
+ private function _getSearchSqls($table)
+ {
+ // Statement types
+ $sqlstr_select = 'SELECT';
+ $sqlstr_delete = 'DELETE';
+ // Table to use
+ $sqlstr_from = ' FROM '
+ . $this->getCommonFunctions()->backquote($GLOBALS['db']) . '.'
+ . $this->getCommonFunctions()->backquote($table);
+ // Gets where clause for the query
+ $where_clause = $this->_getWhereClause($table);
+ // Builds complete queries
+ $sql['select_columns'] = $sqlstr_select . ' * ' . $sqlstr_from . $where_clause;
+ // here, I think we need to still use the COUNT clause, even for
+ // VIEWs, anyway we have a WHERE clause that should limit results
+ $sql['select_count'] = $sqlstr_select . ' COUNT(*) AS `count`'
+ . $sqlstr_from . $where_clause;
+ $sql['delete'] = $sqlstr_delete . $sqlstr_from . $where_clause;
+
+ return $sql;
+ }
+
+ /**
+ * Provides where clause for bulding SQL query
+ *
+ * @param string $table The table name
+ *
+ * @return string The generated where clause
+ */
+ private function _getWhereClause($table)
+ {
+ $where_clause = '';
+ // Columns to select
+ $allColumns = PMA_DBI_get_columns($GLOBALS['db'], $table);
+ $likeClauses = array();
+ // Based on search type, decide like/regex & '%'/''
+ $like_or_regex = (($this->_criteriaSearchType == 4) ? 'REGEXP' : 'LIKE');
+ $automatic_wildcard = (($this->_criteriaSearchType < 3) ? '%' : '');
+ // For "as regular expression" (search option 4), LIKE won't be used
+ // Usage example: If user is seaching for a literal $ in a regexp search,
+ // he should enter \$ as the value.
+ $this->_criteriaSearchString = $this->getCommonFunctions()->sqlAddSlashes(
+ $this->_criteriaSearchString, ($this->_criteriaSearchType == 4 ? false : true)
+ );
+ // Extract search words or pattern
+ $search_words = (($this->_criteriaSearchType > 2)
+ ? array($this->_criteriaSearchString) : explode(' ', $this->_criteriaSearchString));
+
+ foreach ($search_words as $search_word) {
+ // Eliminates empty values
+ if (strlen($search_word) === 0) {
+ continue;
+ }
+ $likeClausesPerColumn = array();
+ // for each column in the table
+ foreach ($allColumns as $column) {
+ if (! isset($this->_criteriaColumnName)
+ || strlen($this->_criteriaColumnName) == 0
+ || $column['Field'] == $this->_criteriaColumnName
+ ) {
+ // Drizzle has no CONVERT and all text columns are UTF-8
+ $column = ((PMA_DRIZZLE)
+ ? $this->getCommonFunctions()->backquote($column['Field'])
+ : 'CONVERT(' . $this->getCommonFunctions()->backquote($column['Field'])
+ . ' USING utf8)');
+ $likeClausesPerColumn[] = $column . ' ' . $like_or_regex . ' '
+ . "'"
+ . $automatic_wildcard . $search_word . $automatic_wildcard
+ . "'";
+ }
+ } // end for
+ if (count($likeClausesPerColumn) > 0) {
+ $likeClauses[] = implode(' OR ', $likeClausesPerColumn);
+ }
+ } // end for
+ // Use 'OR' if 'at least one word' is to be searched, else use 'AND'
+ $implode_str = ($this->_criteriaSearchType == 1 ? ' OR ' : ' AND ');
+ if ( empty($likeClauses)) {
+ // this could happen when the "inside column" does not exist
+ // in any selected tables
+ $where_clause = ' WHERE FALSE';
+ } else {
+ $where_clause = ' WHERE ('
+ . implode(') ' . $implode_str . ' (', $likeClauses)
+ . ')';
+ }
+ return $where_clause;
+ }
+
+ /**
+ * Displays database search results
+ *
+ * @return string HTML for search results
+ */
+ public function getSearchResults()
+ {
+ $html_output = '';
+ // Displays search string
+ $html_output .= ' '
+ . ''
+ . '';
+
+ $num_search_result_total = 0;
+ $odd_row = true;
+ // For each table selected as search criteria
+ foreach ($this->_criteriaTables as $each_table) {
+ // Gets the SQL statements
+ $newsearchsqls = $this->_getSearchSqls($each_table);
+ // Executes the "COUNT" statement
+ $res_cnt = PMA_DBI_fetch_value($newsearchsqls['select_count']);
+ $num_search_result_total += $res_cnt;
+ // Gets the result row's HTML for a table
+ $html_output .= $this->_getResultsRow(
+ $each_table, $newsearchsqls, $odd_row
+ );
+ $odd_row = ! $odd_row;
+ } // end for
+ $html_output .= '
';
+ // Displays total number of matches
+ if (count($this->_criteriaTables) > 1) {
+ $html_output .= '';
+ $html_output .= sprintf(
+ _ngettext(
+ 'Total: %s match',
+ 'Total: %s matches',
+ $num_search_result_total
+ ),
+ $num_search_result_total
+ );
+ $html_output .= '
';
+ }
+ return $html_output;
+ }
+
+ /**
+ * Provides search results row with browse/delete links.
+ * (for a table)
+ *
+ * @param string $each_table Tables on which search is to be performed
+ * @param array $newsearchsqls Contains SQL queries
+ * @param bool $odd_row For displaying contrasting table rows
+ *
+ * @return string HTML row
+ */
+ private function _getResultsRow($each_table, $newsearchsqls, $odd_row)
+ {
+ $this_url_params = array(
+ 'db' => $GLOBALS['db'],
+ 'goto' => 'db_sql.php',
+ 'pos' => 0,
+ 'is_js_confirmed' => 0,
+ );
+ $res_cnt = PMA_DBI_fetch_value($newsearchsqls['select_count']);
+ // Start forming search results row
+ $html_output = '';
+ // Displays results count for a table
+ $html_output .= '';
+ $html_output .= sprintf(
+ _ngettext(
+ '%1$s match in %2$s ',
+ '%1$s matches in %2$s ', $res_cnt
+ ),
+ $res_cnt, htmlspecialchars($each_table)
+ );
+ $html_output .= ' ';
+ // Displays browse/delete link if result count > 0
+ if ($res_cnt > 0) {
+ $this_url_params['sql_query'] = $newsearchsqls['select_columns'];
+ $browse_result_path = 'sql.php' . PMA_generate_common_url($this_url_params);
+ $html_output .= ''
+ . __('Browse') . ' ';
+ $this_url_params['sql_query'] = $newsearchsqls['delete'];
+ $delete_result_path = 'sql.php' . PMA_generate_common_url($this_url_params);
+ $html_output .= ''
+ . __('Delete') . ' ';
+ } else {
+ $html_output .= ' '
+ .' ';
+ }// end if else
+ $html_output .= ' ';
+ return $html_output;
+ }
+
+ /**
+ * Provides the main search form's html
+ *
+ * @param array $url_params URL parameters
+ *
+ * @return string HTML for selection form
+ */
+ public function getSelectionForm($url_params)
+ {
+ $html_output = ' ';
+ $html_output .= '';
+ $html_output .= $this->_getResultDivs();
+
+ return $html_output;
+ }
+
+ /**
+ * Provides div tags for browsing search results and sql query form.
+ *
+ * @return string div tags
+ */
+ private function _getResultDivs()
+ {
+ $html_output = '';
+ $html_output .= '';
+ $html_output .= '
';
+ $html_output .= '
';
+ // div for browsing results
+ $html_output .= '';
+ $html_output .= '';
+ $html_output .= '
';
+ $html_output .= ' ';
+ $html_output .= '';
+ $html_output .= '';
+ $html_output .= '
';
+ $html_output .= '';
+ $html_output .= ' ';
+ return $html_output;
+ }
+}
diff --git a/libraries/DisplayResults.class.php b/libraries/DisplayResults.class.php
new file mode 100644
index 0000000000..eba7600bfe
--- /dev/null
+++ b/libraries/DisplayResults.class.php
@@ -0,0 +1,5728 @@
+$property;
+ }
+ }
+
+
+ /**
+ * Set values for any property of this class
+ *
+ * @param string $property name of the property
+ * @param $value value to set
+ *
+ * @return void
+ */
+ public function __set($property, $value)
+ {
+ if (property_exists($this, $property)) {
+ $this->$property = $value;
+ }
+ }
+
+
+ /**
+ * Get CommmonFunctions
+ *
+ * @return CommonFunctions object
+ */
+ public function getCommonFunctions()
+ {
+ if (is_null($this->_common_functions)) {
+ $this->_common_functions = PMA_CommonFunctions::getInstance();
+ }
+ return $this->_common_functions;
+ }
+
+
+ /**
+ * 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->__set('_db', $db);
+ $this->__set('_table', $table);
+ $this->__set('_goto', $goto);
+ $this->__set('_sql_query', $sql_query);
+ }
+
+
+ /**
+ * 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
+ *
+ * @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
+ ) {
+
+ $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);
+
+ } // 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 ..."
+ *
+ * @param array $analyzed_sql the analyzed query
+ *
+ * @return boolean
+ *
+ * @access private
+ *
+ * @see _getTableHeaders(), _getColumnParams()
+ */
+ private function _isSelect($analyzed_sql)
+ {
+ 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 = '';
+ // for true or 'both'
+ if ($GLOBALS['cfg']['NavigationBarIconic']) {
+ $caption_output .= $caption;
+ }
+
+ // for false or 'both'
+ if (($GLOBALS['cfg']['NavigationBarIconic'] === false)
+ || ($GLOBALS['cfg']['NavigationBarIconic'] === self::POSITION_BOTH)
+ ) {
+ $caption_output .= ' ' . $title;
+ }
+ $title_output = ' title="' . $title . '"';
+
+ return ' '
+ . ''
+ . ' ';
+
+ } // 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 .= ''
+ . ''
+ . ' ';
+
+ // 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 .= '';
+ $_url_params = array(
+ 'db' => $this->__get('_db'),
+ 'table' => $this->__get('_table'),
+ 'sql_query' => $this->__get('_sql_query'),
+ 'goto' => $this->__get('_goto'),
+ );
+
+ //'
+ . ' ';
+ } //_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
+ .= '|
';
+ }
+
+ $table_navigation_html .= ''
+ . ''
+ . ' '
+ . ''
+ . ''
+ . ' ';
+
+ // 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 .= '';
+
+ $table_navigation_html .= ''
+ . ' '
+ . ' '
+ . ' '
+ . '
';
+
+ 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"
+ . ''
+ . ''
+ . ' ';
+ } // 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 = ' ';
+ // 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 .= ' '
+ . ' '
+ . ' '
+ . __('Start row') . ': ' . "\n"
+ . ' '
+ . __('Number of rows') . ': ' . "\n"
+ . ' ';
+
+ 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 .= $this->getCommonFunctions()->getDropdown(
+ 'disp_direction', $choices,
+ $_SESSION['tmp_user_values']['disp_direction'],
+ $id_for_direction_dropdown
+ );
+ unset($choices);
+ }
+
+ $additional_fields_html .= sprintf(
+ __('Headers every %s rows'),
+ ' '
+ );
+
+ 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
+ *
+ * @return string html content
+ *
+ * @access private
+ *
+ * @see getTable()
+ */
+ private function _getTableHeaders(
+ &$is_display, $analyzed_sql = '',
+ $sort_expression = '', $sort_expression_nodirection = '',
+ $sort_direction = ''
+ ) {
+
+ $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
+ = $_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 .= ' '
+ . ''
+ . PMA_generate_common_hidden_inputs(
+ $this->__get('_db'), $this->__get('_table')
+ )
+ . '
';
+
+ // 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'))) {
+
+ $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->_getFeildVisibilityParams(
+ $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[$this->getCommonFunctions()->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') {
+
+ 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'][] = ' ' . "\n" . $order_link . $comments . ' ' . "\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'][] = ' ' . "\n" . ' '
+ . htmlspecialchars($fields_meta[$i]->name)
+ . "\n" . $comments . ' ';
+ } // 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 .= '