From 8dfb964cdf892f52b50f65d271b5b48d75457f68 Mon Sep 17 00:00:00 2001 From: Chanaka Indrajith Date: Sun, 17 Jun 2012 08:34:28 +0530 Subject: [PATCH 01/12] Introduce PMA_CommonFunctions class --- libraries/common.lib.php | 7832 +++++++++++++++++++------------------- libraries/core.lib.php | 27 + 2 files changed, 3935 insertions(+), 3924 deletions(-) diff --git a/libraries/common.lib.php b/libraries/common.lib.php index 964e47fb38..8ecd2f7e57 100644 --- a/libraries/common.lib.php +++ b/libraries/common.lib.php @@ -1,4145 +1,4129 @@ '; - if ($include_icon) { - $button .= PMA_getImage($icon, $alternate); - } - if ($include_icon && $include_text) { - $button .= ' '; - } - if ($include_text) { - $button .= $alternate; - } - $button .= ''; - - return $button; -} - - -/** - * Returns an HTML IMG tag for a particular image from a theme, - * which may be an actual file or an icon from a sprite - * - * @param string $image The name of the file to get - * @param string $alternate Used to set 'alt' and 'title' attributes of the image - * @param array $attributes An associative array of other attributes - * - * @return string an html IMG tag - */ -function PMA_getImage($image, $alternate = '', $attributes = array()) -{ - static $sprites; // cached list of available sprites (if any) - - $url = ''; - $is_sprite = false; - $alternate = htmlspecialchars($alternate); - - // If it's the first time this function is called - if (! isset($sprites)) { - // Try to load the list of sprites - if (is_readable($_SESSION['PMA_Theme']->getPath() . '/sprites.lib.php')) { - include_once $_SESSION['PMA_Theme']->getPath() . '/sprites.lib.php'; - $sprites = PMA_sprites(); + /** + * Detects which function to use for PMA_pow. + * + * @return string Function name. + */ + private function PMA_detectPow() + { + if (function_exists('bcpow')) { + // BCMath Arbitrary Precision Mathematics Function + return 'bcpow'; + } elseif (function_exists('gmp_pow')) { + // GMP Function + return 'gmp_pow'; } else { - // No sprites are available for this theme - $sprites = array(); + // PHP function + return 'pow'; } } - - // Check if we have the requested image as a sprite - // and set $url accordingly - $class = str_replace(array('.gif','.png'), '', $image); - if (array_key_exists($class, $sprites)) { - $is_sprite = true; - $url = (defined('PMA_TEST_THEME') ? '../' : '') . 'themes/dot.gif'; - } else { - $url = $GLOBALS['pmaThemeImage'] . $image; + + + /** + * Exponential expression / raise number into power + * + * @param string $base base to raise + * @param string $exp exponent to use + * @param mixed $use_function pow function to use, or false for auto-detect + * + * @return mixed string or float + */ + public function PMA_pow($base, $exp, $use_function = false) + { + static $pow_function = null; + + if ($pow_function == null) { + $pow_function = $this->PMA_detectPow(); + } + + if (! $use_function) { + $use_function = $pow_function; + } + + if (($exp < 0) && ($use_function != 'pow')) { + return false; + } + + switch ($use_function) { + case 'bcpow' : + // bcscale() needed for testing PMA_pow() with base values < 1 + bcscale(10); + $pow = bcpow($base, $exp); + break; + case 'gmp_pow' : + $pow = gmp_strval(gmp_pow($base, $exp)); + break; + case 'pow' : + $base = (float) $base; + $exp = (int) $exp; + $pow = pow($base, $exp); + break; + default: + $pow = $use_function($base, $exp); + } + + return $pow; } - - // set class attribute - if ($is_sprite) { - if (isset($attributes['class'])) { - $attributes['class'] = "icon ic_$class " . $attributes['class']; + + + /** + * Returns an HTML IMG tag for a particular icon from a theme, + * which may be an actual file or an icon from a sprite. + * This function takes into account the PropertiesIconic + * configuration setting and wraps the image tag in a span tag. + * + * @param string $icon name of icon file + * @param string $alternate alternate text + * @param boolean $force_text whether to force alternate text to be displayed + * + * @return string an html snippet + */ + public function PMA_getIcon($icon, $alternate = '', $force_text = false) + { + // $cfg['PropertiesIconic'] is true or both + $include_icon = ($GLOBALS['cfg']['PropertiesIconic'] !== false); + // $cfg['PropertiesIconic'] is false or both + // OR we have no $include_icon + $include_text = ($force_text || ($GLOBALS['cfg']['PropertiesIconic'] !== true)); + + // Always use a span (we rely on this in js/sql.js) + $button = ''; + if ($include_icon) { + $button .= $this->PMA_getImage($icon, $alternate); + } + if ($include_icon && $include_text) { + $button .= ' '; + } + if ($include_text) { + $button .= $alternate; + } + $button .= ''; + + return $button; + } + + + /** + * Returns an HTML IMG tag for a particular image from a theme, + * which may be an actual file or an icon from a sprite + * + * @param string $image The name of the file to get + * @param string $alternate Used to set 'alt' and 'title' attributes of the image + * @param array $attributes An associative array of other attributes + * + * @return string an html IMG tag + */ + public function PMA_getImage($image, $alternate = '', $attributes = array()) + { + static $sprites; // cached list of available sprites (if any) + + $url = ''; + $is_sprite = false; + $alternate = htmlspecialchars($alternate); + + // If it's the first time this function is called + if (! isset($sprites)) { + // Try to load the list of sprites + if (is_readable($_SESSION['PMA_Theme']->getPath() . '/sprites.lib.php')) { + include_once $_SESSION['PMA_Theme']->getPath() . '/sprites.lib.php'; + $sprites = PMA_sprites(); + } else { + // No sprites are available for this theme + $sprites = array(); + } + } + + // Check if we have the requested image as a sprite + // and set $url accordingly + $class = str_replace(array('.gif','.png'), '', $image); + if (array_key_exists($class, $sprites)) { + $is_sprite = true; + $url = (defined('PMA_TEST_THEME') ? '../' : '') . 'themes/dot.gif'; } else { - $attributes['class'] = "icon ic_$class"; + $url = $GLOBALS['pmaThemeImage'] . $image; } - } - - // set all other attributes - $attr_str = ''; - foreach ($attributes as $key => $value) { - if (! in_array($key, array('alt', 'title'))) { - $attr_str .= " $key=\"$value\""; + + // set class attribute + if ($is_sprite) { + if (isset($attributes['class'])) { + $attributes['class'] = "icon ic_$class " . $attributes['class']; + } else { + $attributes['class'] = "icon ic_$class"; + } } - } - - // override the alt attribute - if (isset($attributes['alt'])) { - $alt = $attributes['alt']; - } else { - $alt = $alternate; - } - - // override the title attribute - if (isset($attributes['title'])) { - $title = $attributes['title']; - } else { - $title = $alternate; - } - - // generate the IMG tag - $template = '%s'; - $retval = sprintf($template, $url, $title, $alt, $attr_str); - return $retval; -} + // set all other attributes + $attr_str = ''; + foreach ($attributes as $key => $value) { + if (! in_array($key, array('alt', 'title'))) { + $attr_str .= " $key=\"$value\""; + } + } + // override the alt attribute + if (isset($attributes['alt'])) { + $alt = $attributes['alt']; + } else { + $alt = $alternate; + } -/** - * Returns the formatted maximum size for an upload - * - * @param integer $max_upload_size the size - * - * @return string the message - * - * @access public - */ -function PMA_getFormattedMaximumUploadSize($max_upload_size) -{ - // I have to reduce the second parameter (sensitiveness) from 6 to 4 - // to avoid weird results like 512 kKib - list($max_size, $max_unit) = PMA_formatByteDown($max_upload_size, 4); - return '(' . sprintf(__('Max: %s%s'), $max_size, $max_unit) . ')'; -} + // override the title attribute + if (isset($attributes['title'])) { + $title = $attributes['title']; + } else { + $title = $alternate; + } + // generate the IMG tag + $template = '%s'; + $retval = sprintf($template, $url, $title, $alt, $attr_str); -/** - * Generates a hidden field which should indicate to the browser - * the maximum size for upload - * - * @param integer $max_size the size - * - * @return string the INPUT field - * - * @access public - */ -function PMA_generateHiddenMaxFileSize($max_size) -{ - return ''; -} - - -/** - * Add slashes before "'" and "\" characters so a value containing them can - * be used in a sql comparison. - * - * @param string $a_string the string to slash - * @param bool $is_like whether the string will be used in a 'LIKE' clause - * (it then requires two more escaped sequences) or not - * @param bool $crlf whether to treat cr/lfs as escape-worthy entities - * (converts \n to \\n, \r to \\r) - * @param bool $php_code whether this function is used as part of the - * "Create PHP code" dialog - * - * @return string the slashed string - * - * @access public - */ -function PMA_sqlAddSlashes( - $a_string = '', $is_like = false, $crlf = false, $php_code = false -) { - - if ($is_like) { - $a_string = str_replace('\\', '\\\\\\\\', $a_string); - } else { - $a_string = str_replace('\\', '\\\\', $a_string); + return $retval; } - if ($crlf) { - $a_string = strtr( - $a_string, - array("\n" => '\n', "\r" => '\r', "\t" => '\t') - ); + + /** + * Returns the formatted maximum size for an upload + * + * @param integer $max_upload_size the size + * + * @return string the message + * + * @access public + */ + public function PMA_getFormattedMaximumUploadSize($max_upload_size) + { + // I have to reduce the second parameter (sensitiveness) from 6 to 4 + // to avoid weird results like 512 kKib + list($max_size, $max_unit) = $this->PMA_formatByteDown($max_upload_size, 4); + return '(' . sprintf(__('Max: %s%s'), $max_size, $max_unit) . ')'; } - if ($php_code) { - $a_string = str_replace('\'', '\\\'', $a_string); - } else { - $a_string = str_replace('\'', '\'\'', $a_string); + + /** + * Generates a hidden field which should indicate to the browser + * the maximum size for upload + * + * @param integer $max_size the size + * + * @return string the INPUT field + * + * @access public + */ + public function PMA_generateHiddenMaxFileSize($max_size) + { + return ''; } - return $a_string; -} // end of the 'PMA_sqlAddSlashes()' function + /** + * Add slashes before "'" and "\" characters so a value containing them can + * be used in a sql comparison. + * + * @param string $a_string the string to slash + * @param bool $is_like whether the string will be used in a 'LIKE' clause + * (it then requires two more escaped sequences) or not + * @param bool $crlf whether to treat cr/lfs as escape-worthy entities + * (converts \n to \\n, \r to \\r) + * @param bool $php_code whether this function is used as part of the + * "Create PHP code" dialog + * + * @return string the slashed string + * + * @access public + */ + public function PMA_sqlAddSlashes( + $a_string = '', $is_like = false, $crlf = false, $php_code = false + ) { -/** - * Add slashes before "_" and "%" characters for using them in MySQL - * database, table and field names. - * Note: This function does not escape backslashes! - * - * @param string $name the string to escape - * - * @return string the escaped string - * - * @access public - */ -function PMA_escapeMysqlWildcards($name) -{ - return strtr($name, array('_' => '\\_', '%' => '\\%')); -} // end of the 'PMA_escapeMysqlWildcards()' function + if ($is_like) { + $a_string = str_replace('\\', '\\\\\\\\', $a_string); + } else { + $a_string = str_replace('\\', '\\\\', $a_string); + } - -/** - * removes slashes before "_" and "%" characters - * Note: This function does not unescape backslashes! - * - * @param string $name the string to escape - * - * @return string the escaped string - * - * @access public - */ -function PMA_unescapeMysqlWildcards($name) -{ - return strtr($name, array('\\_' => '_', '\\%' => '%')); -} // end of the 'PMA_unescapeMysqlWildcards()' function - - -/** - * removes quotes (',",`) from a quoted string - * - * checks if the sting is quoted and removes this quotes - * - * @param string $quoted_string string to remove quotes from - * @param string $quote type of quote to remove - * - * @return string unqoted string - */ -function PMA_unQuote($quoted_string, $quote = null) -{ - $quotes = array(); - - if ($quote === null) { - $quotes[] = '`'; - $quotes[] = '"'; - $quotes[] = "'"; - } else { - $quotes[] = $quote; - } - - foreach ($quotes as $quote) { - if (substr($quoted_string, 0, 1) === $quote - && substr($quoted_string, -1, 1) === $quote - ) { - $unquoted_string = substr($quoted_string, 1, -1); - // replace escaped quotes - $unquoted_string = str_replace( - $quote . $quote, - $quote, - $unquoted_string + if ($crlf) { + $a_string = strtr( + $a_string, + array("\n" => '\n', "\r" => '\r', "\t" => '\t') ); - return $unquoted_string; } - } - return $quoted_string; -} - - -/** - * format sql strings - * - * @param mixed $parsed_sql pre-parsed SQL structure - * @param string $unparsed_sql raw SQL string - * - * @return string the formatted sql - * - * @global array the configuration array - * @global boolean whether the current statement is a multiple one or not - * - * @access public - * @todo move into PMA_Sql - */ -function PMA_formatSql($parsed_sql, $unparsed_sql = '') -{ - global $cfg; - - // Check that we actually have a valid set of parsed data - // well, not quite - // first check for the SQL parser having hit an error - if (PMA_SQP_isError()) { - return htmlspecialchars($parsed_sql['raw']); - } - // then check for an array - if (! is_array($parsed_sql)) { - // We don't so just return the input directly - // This is intended to be used for when the SQL Parser is turned off - $formatted_sql = "
\n";
-        if (($cfg['SQP']['fmtType'] == 'none') && ($unparsed_sql != '')) {
-            $formatted_sql .= $unparsed_sql;
+        if ($php_code) {
+            $a_string = str_replace('\'', '\\\'', $a_string);
         } else {
-            $formatted_sql .= $parsed_sql;
+            $a_string = str_replace('\'', '\'\'', $a_string);
         }
-        $formatted_sql .= "\n
"; - return $formatted_sql; - } - $formatted_sql = ''; + return $a_string; + } // end of the 'PMA_sqlAddSlashes()' function - switch ($cfg['SQP']['fmtType']) { - case 'none': - if ($unparsed_sql != '') { - $formatted_sql = '
' . "\n"
-                . PMA_SQP_formatNone(array('raw' => $unparsed_sql)) . "\n"
-                . '
'; + + /** + * Add slashes before "_" and "%" characters for using them in MySQL + * database, table and field names. + * Note: This function does not escape backslashes! + * + * @param string $name the string to escape + * + * @return string the escaped string + * + * @access public + */ + public function PMA_escapeMysqlWildcards($name) + { + return strtr($name, array('_' => '\\_', '%' => '\\%')); + } // end of the 'PMA_escapeMysqlWildcards()' function + + + /** + * removes slashes before "_" and "%" characters + * Note: This function does not unescape backslashes! + * + * @param string $name the string to escape + * + * @return string the escaped string + * + * @access public + */ + public function PMA_unescapeMysqlWildcards($name) + { + return strtr($name, array('\\_' => '_', '\\%' => '%')); + } // end of the 'PMA_unescapeMysqlWildcards()' function + + + /** + * removes quotes (',",`) from a quoted string + * + * checks if the sting is quoted and removes this quotes + * + * @param string $quoted_string string to remove quotes from + * @param string $quote type of quote to remove + * + * @return string unqoted string + */ + public function PMA_unQuote($quoted_string, $quote = null) + { + $quotes = array(); + + if ($quote === null) { + $quotes[] = '`'; + $quotes[] = '"'; + $quotes[] = "'"; } else { - $formatted_sql = PMA_SQP_formatNone($parsed_sql); - } - break; - case 'html': - $formatted_sql = PMA_SQP_formatHtml($parsed_sql, 'color'); - break; - case 'text': - $formatted_sql = PMA_SQP_formatHtml($parsed_sql, 'text'); - break; - default: - break; - } // end switch - - return $formatted_sql; -} // end of the "PMA_formatSql()" function - - -/** - * Displays a link to the documentation as an icon - * - * @param string $link documentation link - * @param string $target optional link target - * - * @return string the html link - * - * @access public - */ -function PMA_showDocLink($link, $target = 'documentation') -{ - return '' - . PMA_getImage('b_help.png', __('Documentation')) - . ''; -} // end of the 'PMA_showDocLink()' function - - -/** - * Displays a link to the official MySQL documentation - * - * @param string $chapter chapter of "HTML, one page per chapter" documentation - * @param string $link contains name of page/anchor that is being linked - * @param bool $big_icon whether to use big icon (like in left frame) - * @param string $anchor anchor to page part - * @param bool $just_open whether only the opening tag should be returned - * - * @return string the html link - * - * @access public - */ -function PMA_showMySQLDocu( - $chapter, $link, $big_icon = false, $anchor = '', $just_open = false -) { - - global $cfg; - - if (($cfg['MySQLManualType'] == 'none') || empty($cfg['MySQLManualBase'])) { - return ''; - } - - // Fixup for newly used names: - $chapter = str_replace('_', '-', strtolower($chapter)); - $link = str_replace('_', '-', strtolower($link)); - - switch ($cfg['MySQLManualType']) { - case 'chapters': - if (empty($chapter)) { - $chapter = 'index'; - } - if (empty($anchor)) { - $anchor = $link; - } - $url = $cfg['MySQLManualBase'] . '/' . $chapter . '.html#' . $anchor; - break; - case 'big': - if (empty($anchor)) { - $anchor = $link; - } - $url = $cfg['MySQLManualBase'] . '#' . $anchor; - break; - case 'searchable': - if (empty($link)) { - $link = 'index'; - } - $url = $cfg['MySQLManualBase'] . '/' . $link . '.html'; - if (! empty($anchor)) { - $url .= '#' . $anchor; - } - break; - case 'viewable': - default: - if (empty($link)) { - $link = 'index'; - } - $mysql = '5.0'; - $lang = 'en'; - if (defined('PMA_MYSQL_INT_VERSION')) { - if (PMA_MYSQL_INT_VERSION >= 50500) { - $mysql = '5.5'; - /* l10n: Please check that translation actually exists. */ - $lang = _pgettext('MySQL 5.5 documentation language', 'en'); - } else if (PMA_MYSQL_INT_VERSION >= 50100) { - $mysql = '5.1'; - /* l10n: Please check that translation actually exists. */ - $lang = _pgettext('MySQL 5.1 documentation language', 'en'); - } else { - $mysql = '5.0'; - /* l10n: Please check that translation actually exists. */ - $lang = _pgettext('MySQL 5.0 documentation language', 'en'); - } - } - $url = $cfg['MySQLManualBase'] - . '/' . $mysql . '/' . $lang . '/' . $link . '.html'; - if (! empty($anchor)) { - $url .= '#' . $anchor; - } - break; - } - - $open_link = ''; - if ($just_open) { - return $open_link; - } elseif ($big_icon) { - return $open_link - . PMA_getImage('b_sqlhelp.png', __('Documentation')) . ''; - } else { - return PMA_showDocLink(PMA_linkURL($url), 'mysql_doc'); - } - -} // end of the 'PMA_showMySQLDocu()' function - - -/** - * Displays a link to the phpMyAdmin documentation - * - * @param string $anchor anchor in documentation - * - * @return string the html link - * - * @access public - */ -function PMA_showDocu($anchor) -{ - return PMA_showDocLink('Documentation.html#' . $anchor); -} // end of the 'PMA_showDocu()' function - - -/** - * Displays a link to the PHP documentation - * - * @param string $target anchor in documentation - * - * @return string the html link - * - * @access public - */ -function PMA_showPHPDocu($target) -{ - $url = PMA_getPHPDocLink($target); - - return PMA_showDocLink($url); -} // end of the 'PMA_showPHPDocu()' function - - -/** - * Returns HTML code for a tooltip - * - * @param string $message the message for the tooltip - * - * @return string - * - * @access public - */ -function PMA_showHint($message) -{ - return '' - . PMA_getImage('b_help.png') - . '' . $message . '' - . ''; -} - - -/** - * Displays a MySQL error message in the right frame. - * - * @param string $error_message the error message - * @param string $the_query the sql query that failed - * @param bool $is_modify_link whether to show a "modify" link or not - * @param string $back_url the "back" link url (full path is not required) - * @param bool $exit EXIT the page? - * - * @return void - * - * @global string the curent table - * @global string the current db - * - * @access public - */ -function PMA_mysqlDie( - $error_message = '', $the_query = '', - $is_modify_link = true, $back_url = '', $exit = true -) { - - global $table, $db; - - $error_msg = ''; - - if (! $error_message) { - $error_message = PMA_DBI_getError(); - } - if (! $the_query && ! empty($GLOBALS['sql_query'])) { - $the_query = $GLOBALS['sql_query']; - } - - // --- Added to solve bug #641765 - if (! function_exists('PMA_SQP_isError') || PMA_SQP_isError()) { - $formatted_sql = htmlspecialchars($the_query); - } elseif (empty($the_query) || (trim($the_query) == '')) { - $formatted_sql = ''; - } else { - if (strlen($the_query) > $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']) { - $formatted_sql = htmlspecialchars( - substr($the_query, 0, $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']) - ) - . '[...]'; - } else { - $formatted_sql = PMA_formatSql(PMA_SQP_parse($the_query), $the_query); - } - } - // --- - $error_msg .= "\n" . '' . "\n"; - $error_msg .= '

' . __('Error') - . '

' . "\n"; - // if the config password is wrong, or the MySQL server does not - // respond, do not show the query that would reveal the - // username/password - if (! empty($the_query) && ! strstr($the_query, 'connect')) { - // --- Added to solve bug #641765 - if (function_exists('PMA_SQP_isError') && PMA_SQP_isError()) { - $error_msg .= PMA_SQP_getErrorString() . "\n"; - $error_msg .= '
' . "\n"; - } - // --- - // modified to show the help on sql errors - $error_msg .= '

' . __('SQL query') . ':' . "\n"; - if (strstr(strtolower($formatted_sql), 'select')) { - // please show me help to the error on select - $error_msg .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT'); - } - if ($is_modify_link) { - $_url_params = array( - 'sql_query' => $the_query, - 'show_query' => 1, - ); - if (strlen($table)) { - $_url_params['db'] = $db; - $_url_params['table'] = $table; - $doedit_goto = ''; - } elseif (strlen($db)) { - $_url_params['db'] = $db; - $doedit_goto = ''; - } else { - $doedit_goto = ''; - } - - $error_msg .= $doedit_goto - . PMA_getIcon('b_edit.png', __('Edit')) - . ''; - } // end if - $error_msg .= '

' . "\n" - .'

' . "\n" - . $formatted_sql . "\n" - . '

' . "\n"; - } // end if - - if (! empty($error_message)) { - $error_message = preg_replace( - "@((\015\012)|(\015)|(\012)){3,}@", - "\n\n", - $error_message - ); - } - // modified to show the help on error-returns - // (now error-messages-server) - $error_msg .= '

' . "\n" - . ' ' . __('MySQL said: ') . '' - . PMA_showMySQLDocu('Error-messages-server', 'Error-messages-server') - . "\n" - . '

' . "\n"; - - // The error message will be displayed within a CODE segment. - // To preserve original formatting, but allow wordwrapping, - // we do a couple of replacements - - // Replace all non-single blanks with their HTML-counterpart - $error_message = str_replace(' ', '  ', $error_message); - // Replace TAB-characters with their HTML-counterpart - $error_message = str_replace("\t", '    ', $error_message); - // Replace linebreaks - $error_message = nl2br($error_message); - - $error_msg .= '' . "\n" - . $error_message . "\n" - . '
' . "\n"; - $error_msg .= '
'; - - $_SESSION['Import_message']['message'] = $error_msg; - - if ($exit) { - /** - * If in an Ajax request - * - avoid displaying a Back link - * - use PMA_Response() to transmit the message and exit - */ - if ($GLOBALS['is_ajax_request'] == true) { - $response = PMA_Response::getInstance(); - $response->isSuccess(false); - $response->addJSON('message', $error_msg); - exit; - } - if (! empty($back_url)) { - if (strstr($back_url, '?')) { - $back_url .= '&no_history=true'; - } else { - $back_url .= '?no_history=true'; - } - - $_SESSION['Import_message']['go_back_url'] = $back_url; - - $error_msg .= '
'; - $error_msg .= '[ ' . __('Back') . ' ]'; - $error_msg .= '
' . "\n\n"; + $quotes[] = $quote; } - echo $error_msg; - exit; - } else { - echo $error_msg; - } -} // end of the 'PMA_mysqlDie()' function - - -/** - * returns array with tables of given db with extended information and grouped - * - * @param string $db name of db - * @param string $tables name of tables - * @param integer $limit_offset list offset - * @param int|bool $limit_count max tables to return - * - * @return array (recursive) grouped table list - */ -function PMA_getTableList($db, $tables = null, $limit_offset = 0, - $limit_count = false -) { - $sep = $GLOBALS['cfg']['LeftFrameTableSeparator']; - - if ($tables === null) { - $tables = PMA_DBI_get_tables_full( - $db, false, false, null, $limit_offset, $limit_count - ); - if ($GLOBALS['cfg']['NaturalOrder']) { - uksort($tables, 'strnatcasecmp'); - } - } - - if (count($tables) < 1) { - return $tables; - } - - $default = array( - 'Name' => '', - 'Rows' => 0, - 'Comment' => '', - 'disp_name' => '', - ); - - $table_groups = array(); - - foreach ($tables as $table_name => $table) { - // check for correct row count - if ($table['Rows'] === null) { - // Do not check exact row count here, - // if row count is invalid possibly the table is defect - // and this would break left frame; - // but we can check row count if this is a view or the - // information_schema database - // since PMA_Table::countRecords() returns a limited row count - // in this case. - - // set this because PMA_Table::countRecords() can use it - $tbl_is_view = $table['TABLE_TYPE'] == 'VIEW'; - - if ($tbl_is_view || PMA_is_system_schema($db)) { - $table['Rows'] = PMA_Table::countRecords( - $db, - $table['Name'], - false, - true + foreach ($quotes as $quote) { + if (substr($quoted_string, 0, 1) === $quote + && substr($quoted_string, -1, 1) === $quote + ) { + $unquoted_string = substr($quoted_string, 1, -1); + // replace escaped quotes + $unquoted_string = str_replace( + $quote . $quote, + $quote, + $unquoted_string ); + return $unquoted_string; } } - // in $group we save the reference to the place in $table_groups - // where to store the table info - if ($GLOBALS['cfg']['LeftFrameDBTree'] - && $sep && strstr($table_name, $sep) - ) { - - $parts = explode($sep, $table_name); + return $quoted_string; + } - $group =& $table_groups; - $i = 0; - $group_name_full = ''; - $parts_cnt = count($parts) - 1; - - while (($i < $parts_cnt) - && ($i < $GLOBALS['cfg']['LeftFrameTableLevel']) - ) { - - $group_name = $parts[$i] . $sep; - $group_name_full .= $group_name; - if (! isset($group[$group_name])) { - - $group[$group_name] = array(); - $group[$group_name]['is' . $sep . 'group'] = true; - $group[$group_name]['tab' . $sep . 'count'] = 1; - $group[$group_name]['tab' . $sep . 'group'] = $group_name_full; - - } elseif (! isset($group[$group_name]['is' . $sep . 'group'])) { - - $table = $group[$group_name]; - $group[$group_name] = array(); - $group[$group_name][$group_name] = $table; - unset($table); - $group[$group_name]['is' . $sep . 'group'] = true; - $group[$group_name]['tab' . $sep . 'count'] = 1; - $group[$group_name]['tab' . $sep . 'group'] = $group_name_full; - + /** + * format sql strings + * + * @param mixed $parsed_sql pre-parsed SQL structure + * @param string $unparsed_sql raw SQL string + * + * @return string the formatted sql + * + * @global array the configuration array + * @global boolean whether the current statement is a multiple one or not + * + * @access public + * @todo move into PMA_Sql + */ + public function PMA_formatSql($parsed_sql, $unparsed_sql = '') + { + global $cfg; + + // Check that we actually have a valid set of parsed data + // well, not quite + // first check for the SQL parser having hit an error + if (PMA_SQP_isError()) { + return htmlspecialchars($parsed_sql['raw']); + } + // then check for an array + if (! is_array($parsed_sql)) { + // We don't so just return the input directly + // This is intended to be used for when the SQL Parser is turned off + $formatted_sql = "
\n";
+            if (($cfg['SQP']['fmtType'] == 'none') && ($unparsed_sql != '')) {
+                $formatted_sql .= $unparsed_sql;
+            } else {
+                $formatted_sql .= $parsed_sql;
+            }
+            $formatted_sql .= "\n
"; + return $formatted_sql; + } + + $formatted_sql = ''; + + switch ($cfg['SQP']['fmtType']) { + case 'none': + if ($unparsed_sql != '') { + $formatted_sql = '
' . "\n"
+                    . PMA_SQP_formatNone(array('raw' => $unparsed_sql)) . "\n"
+                    . '
'; + } else { + $formatted_sql = PMA_SQP_formatNone($parsed_sql); + } + break; + case 'html': + $formatted_sql = PMA_SQP_formatHtml($parsed_sql, 'color'); + break; + case 'text': + $formatted_sql = PMA_SQP_formatHtml($parsed_sql, 'text'); + break; + default: + break; + } // end switch + + return $formatted_sql; + } // end of the "PMA_formatSql()" function + + + /** + * Displays a link to the documentation as an icon + * + * @param string $link documentation link + * @param string $target optional link target + * + * @return string the html link + * + * @access public + */ + public function PMA_showDocLink($link, $target = 'documentation') + { + return '' + . $this->PMA_getImage('b_help.png', __('Documentation')) + . ''; + } // end of the 'PMA_showDocLink()' function + + + /** + * Displays a link to the official MySQL documentation + * + * @param string $chapter chapter of "HTML, one page per chapter" documentation + * @param string $link contains name of page/anchor that is being linked + * @param bool $big_icon whether to use big icon (like in left frame) + * @param string $anchor anchor to page part + * @param bool $just_open whether only the opening tag should be returned + * + * @return string the html link + * + * @access public + */ + public function PMA_showMySQLDocu( + $chapter, $link, $big_icon = false, $anchor = '', $just_open = false + ) { + + global $cfg; + + if (($cfg['MySQLManualType'] == 'none') || empty($cfg['MySQLManualBase'])) { + return ''; + } + + // Fixup for newly used names: + $chapter = str_replace('_', '-', strtolower($chapter)); + $link = str_replace('_', '-', strtolower($link)); + + switch ($cfg['MySQLManualType']) { + case 'chapters': + if (empty($chapter)) { + $chapter = 'index'; + } + if (empty($anchor)) { + $anchor = $link; + } + $url = $cfg['MySQLManualBase'] . '/' . $chapter . '.html#' . $anchor; + break; + case 'big': + if (empty($anchor)) { + $anchor = $link; + } + $url = $cfg['MySQLManualBase'] . '#' . $anchor; + break; + case 'searchable': + if (empty($link)) { + $link = 'index'; + } + $url = $cfg['MySQLManualBase'] . '/' . $link . '.html'; + if (! empty($anchor)) { + $url .= '#' . $anchor; + } + break; + case 'viewable': + default: + if (empty($link)) { + $link = 'index'; + } + $mysql = '5.0'; + $lang = 'en'; + if (defined('PMA_MYSQL_INT_VERSION')) { + if (PMA_MYSQL_INT_VERSION >= 50500) { + $mysql = '5.5'; + /* l10n: Please check that translation actually exists. */ + $lang = _pgettext('MySQL 5.5 documentation language', 'en'); + } else if (PMA_MYSQL_INT_VERSION >= 50100) { + $mysql = '5.1'; + /* l10n: Please check that translation actually exists. */ + $lang = _pgettext('MySQL 5.1 documentation language', 'en'); } else { - $group[$group_name]['tab' . $sep . 'count']++; + $mysql = '5.0'; + /* l10n: Please check that translation actually exists. */ + $lang = _pgettext('MySQL 5.0 documentation language', 'en'); } - - $group =& $group[$group_name]; - $i++; } - - } else { - if (! isset($table_groups[$table_name])) { - $table_groups[$table_name] = array(); + $url = $cfg['MySQLManualBase'] + . '/' . $mysql . '/' . $lang . '/' . $link . '.html'; + if (! empty($anchor)) { + $url .= '#' . $anchor; } - $group =& $table_groups; + break; } - - if ($GLOBALS['cfg']['ShowTooltipAliasTB'] - && $GLOBALS['cfg']['ShowTooltipAliasTB'] !== 'nested' - && $table['Comment'] // do not switch if the comment is empty - && $table['Comment'] != 'VIEW' // happens in MySQL 5.1 - ) { - // switch tooltip and name - $table['disp_name'] = $table['Comment']; - $table['Comment'] = $table['Name']; + $open_link = ''; + if ($just_open) { + return $open_link; + } elseif ($big_icon) { + return $open_link + . $this->PMA_getImage('b_sqlhelp.png', __('Documentation')) . ''; } else { - $table['disp_name'] = $table['Name']; + return $this->PMA_showDocLink(PMA_linkURL($url), 'mysql_doc'); } - $group[$table_name] = array_merge($default, $table); - } + } // end of the 'PMA_showMySQLDocu()' function - return $table_groups; -} - -/* ----------------------- Set of misc functions ----------------------- */ - - -/** - * Adds backquotes on both sides of a database, table or field name. - * and escapes backquotes inside the name with another backquote - * - * example: - * - * echo PMA_backquote('owner`s db'); // `owner``s db` - * - * - * - * @param mixed $a_name the database, table or field name to "backquote" - * or array of it - * @param boolean $do_it a flag to bypass this function (used by dump - * functions) - * - * @return mixed the "backquoted" database, table or field name - * - * @access public - */ -function PMA_backquote($a_name, $do_it = true) -{ - - if (is_array($a_name)) { - foreach ($a_name as &$data) { - $data = PMA_backquote($data, $do_it); - } - return $a_name; - } - - if (! $do_it) { - global $PMA_SQPdata_forbidden_word; - - if (! in_array(strtoupper($a_name), $PMA_SQPdata_forbidden_word)) { - return $a_name; - } - } - - // '0' is also empty for php :-( - if (strlen($a_name) && $a_name !== '*') { - return '`' . str_replace('`', '``', $a_name) . '`'; - } else { - return $a_name; - } - -} // end of the 'PMA_backquote()' function - - -/** - * Defines the value depending on the user OS. - * - * @return string the value to use - * - * @access public - */ -function PMA_whichCrlf() -{ - // The 'PMA_USR_OS' constant is defined in "libraries/Config.class.php" - // Win case - if (PMA_USR_OS == 'Win') { - $the_crlf = "\r\n"; - } else { - // Others - $the_crlf = "\n"; - } - - return $the_crlf; -} // end of the 'PMA_whichCrlf()' function - - -/** - * Reloads navigation if needed. - * - * @param bool $jsonly prints out pure JavaScript - * - * @return void - * - * @access public - */ -function PMA_getReloadNavigationScript($jsonly = false) -{ - $retval = ''; - // Reloads the navigation frame via JavaScript if required - if (isset($GLOBALS['reload']) && $GLOBALS['reload']) { - // one of the reasons for a reload is when a table is dropped - // in this case, get rid of the table limit offset, otherwise - // we have a problem when dropping a table on the last page - // and the offset becomes greater than the total number of tables - unset($_SESSION['tmp_user_values']['table_limit_offset']); - $reload_url = './navigation.php?' . PMA_generate_common_url( - $GLOBALS['db'], - '', - '&' - ); - - if (! $jsonly) { - $retval .= '' . PHP_EOL; - } - - unset($GLOBALS['reload']); - } - return $retval; -} - - -/** - * Prepare the message and the query - * usually the message is the result of the query executed - * - * @param string $message the message to display - * @param string $sql_query the query to display - * @param string $type the type (level) of the message - * @param boolean $is_view is this a message after a VIEW operation? - * - * @return string - * - * @access public - */ -function PMA_getMessage( - $message, $sql_query = null, $type = 'notice', $is_view = false -) { - - global $cfg; - $retval = ''; - - if (null === $sql_query) { - if (! empty($GLOBALS['display_query'])) { - $sql_query = $GLOBALS['display_query']; - } elseif ($cfg['SQP']['fmtType'] == 'none' - && ! empty($GLOBALS['unparsed_sql']) - ) { - $sql_query = $GLOBALS['unparsed_sql']; - } elseif (! empty($GLOBALS['sql_query'])) { - $sql_query = $GLOBALS['sql_query']; - } else { - $sql_query = ''; - } - } - - if (isset($GLOBALS['using_bookmark_message'])) { - $retval .= $GLOBALS['using_bookmark_message']->getDisplay(); - unset($GLOBALS['using_bookmark_message']); - } - - // Corrects the tooltip text via JS if required - // @todo this is REALLY the wrong place to do this - very unexpected here - if (! $is_view && strlen($GLOBALS['table']) && $cfg['ShowTooltip']) { - $tooltip = PMA_Table::sGetToolTip($GLOBALS['db'], $GLOBALS['table']); - $uni_tbl = PMA_jsFormat($GLOBALS['db'] . '.' . $GLOBALS['table'], false); - $retval .= "\n"; - $retval .= '' . "\n"; - } // end if ... elseif - - // In an Ajax request, $GLOBALS['cell_align_left'] may not be defined. Hence, - // check for it's presence before using it - $retval .= '
' . "\n"; - - if ($message instanceof PMA_Message) { - if (isset($GLOBALS['special_message'])) { - $message->addMessage($GLOBALS['special_message']); - unset($GLOBALS['special_message']); - } - $retval .= $message->getDisplay(); - } else { - $retval .= '
'; - $retval .= PMA_sanitize($message); - if (isset($GLOBALS['special_message'])) { - $retval .= PMA_sanitize($GLOBALS['special_message']); - unset($GLOBALS['special_message']); - } - $retval .= '
'; - } - - if ($cfg['ShowSQL'] == true && ! empty($sql_query)) { - // Html format the query to be displayed - // If we want to show some sql code it is easiest to create it here - /* SQL-Parser-Analyzer */ - - if (! empty($GLOBALS['show_as_php'])) { - $new_line = '\\n"
' . "\n" - . '    . "'; - $query_base = htmlspecialchars(addslashes($sql_query)); - $query_base = preg_replace( - '/((\015\012)|(\015)|(\012))/', $new_line, $query_base - ); - } else { - $query_base = $sql_query; - } - - $query_too_big = false; - - if (strlen($query_base) > $cfg['MaxCharactersInDisplayedSQL']) { - // when the query is large (for example an INSERT of binary - // data), the parser chokes; so avoid parsing the query - $query_too_big = true; - $shortened_query_base = nl2br( - htmlspecialchars( - substr($sql_query, 0, $cfg['MaxCharactersInDisplayedSQL']) - . '[...]' - ) - ); - } elseif (! empty($GLOBALS['parsed_sql']) - && $query_base == $GLOBALS['parsed_sql']['raw']) { - // (here, use "! empty" because when deleting a bookmark, - // $GLOBALS['parsed_sql'] is set but empty - $parsed_sql = $GLOBALS['parsed_sql']; - } else { - // Parse SQL if needed - $parsed_sql = PMA_SQP_parse($query_base); - } - - // Analyze it - if (isset($parsed_sql) && ! PMA_SQP_isError()) { - $analyzed_display_query = PMA_SQP_analyze($parsed_sql); - - // Same as below (append LIMIT), append the remembered ORDER BY - if ($GLOBALS['cfg']['RememberSorting'] - && isset($analyzed_display_query[0]['queryflags']['select_from']) - && isset($GLOBALS['sql_order_to_append']) - ) { - $query_base = $analyzed_display_query[0]['section_before_limit'] - . "\n" . $GLOBALS['sql_order_to_append'] - . $analyzed_display_query[0]['limit_clause'] . ' ' - . $analyzed_display_query[0]['section_after_limit']; - - // Need to reparse query - $parsed_sql = PMA_SQP_parse($query_base); - // update the $analyzed_display_query - $analyzed_display_query[0]['section_before_limit'] - .= $GLOBALS['sql_order_to_append']; - $analyzed_display_query[0]['order_by_clause'] - = $GLOBALS['sorted_col']; - } - - // Here we append the LIMIT added for navigation, to - // enable its display. Adding it higher in the code - // to $sql_query would create a problem when - // using the Refresh or Edit links. - - // Only append it on SELECTs. - - /** - * @todo what would be the best to do when someone hits Refresh: - * use the current LIMITs ? - */ - - if (isset($analyzed_display_query[0]['queryflags']['select_from']) - && isset($GLOBALS['sql_limit_to_append']) - ) { - $query_base = $analyzed_display_query[0]['section_before_limit'] - . "\n" . $GLOBALS['sql_limit_to_append'] - . $analyzed_display_query[0]['section_after_limit']; - // Need to reparse query - $parsed_sql = PMA_SQP_parse($query_base); - } - } - - if (! empty($GLOBALS['show_as_php'])) { - $query_base = '$sql = "' . $query_base; - } elseif (! empty($GLOBALS['validatequery'])) { - try { - $query_base = PMA_validateSQL($query_base); - } catch (Exception $e) { - $retval .= PMA_Message::error( - __('Failed to connect to SQL validator!') - )->getDisplay(); - } - } elseif (isset($parsed_sql)) { - $query_base = PMA_formatSql($parsed_sql, $query_base); - } - - // Prepares links that may be displayed to edit/explain the query - // (don't go to default pages, we must go to the page - // where the query box is available) - - // Basic url query part - $url_params = array(); - if (! isset($GLOBALS['db'])) { - $GLOBALS['db'] = ''; - } - if (strlen($GLOBALS['db'])) { - $url_params['db'] = $GLOBALS['db']; - if (strlen($GLOBALS['table'])) { - $url_params['table'] = $GLOBALS['table']; - $edit_link = 'tbl_sql.php'; - } else { - $edit_link = 'db_sql.php'; - } - } else { - $edit_link = 'server_sql.php'; - } - - // Want to have the query explained - // but only explain a SELECT (that has not been explained) - /* SQL-Parser-Analyzer */ - $explain_link = ''; - $is_select = false; - if (! empty($cfg['SQLQuery']['Explain']) && ! $query_too_big) { - $explain_params = $url_params; - // Detect if we are validating as well - // To preserve the validate uRL data - if (! empty($GLOBALS['validatequery'])) { - $explain_params['validatequery'] = 1; - } - if (preg_match('@^SELECT[[:space:]]+@i', $sql_query)) { - $explain_params['sql_query'] = 'EXPLAIN ' . $sql_query; - $_message = __('Explain SQL'); - $is_select = true; - } elseif ( - preg_match('@^EXPLAIN[[:space:]]+SELECT[[:space:]]+@i', $sql_query) - ) { - $explain_params['sql_query'] = substr($sql_query, 8); - $_message = __('Skip Explain SQL'); - } - if (isset($explain_params['sql_query'])) { - $explain_link = 'import.php' - . PMA_generate_common_url($explain_params); - $explain_link = ' [' - . PMA_linkOrButton($explain_link, $_message) . ']'; - } - } //show explain - - $url_params['sql_query'] = $sql_query; - $url_params['show_query'] = 1; - - // even if the query is big and was truncated, offer the chance - // to edit it (unless it's enormous, see PMA_linkOrButton() ) - if (! empty($cfg['SQLQuery']['Edit'])) { - if ($cfg['EditInWindow'] == true) { - $onclick = 'window.parent.focus_querywindow(\'' - . PMA_jsFormat($sql_query, false) . '\'); return false;'; - } else { - $onclick = ''; - } - - $edit_link .= PMA_generate_common_url($url_params) . '#querybox'; - $edit_link = ' [' - . PMA_linkOrButton( - $edit_link, __('Edit'), array('onclick' => $onclick) - ) - . ']'; - } else { - $edit_link = ''; - } - - // Also we would like to get the SQL formed in some nice - // php-code - if (! empty($cfg['SQLQuery']['ShowAsPHP']) && ! $query_too_big) { - $php_params = $url_params; - - if (! empty($GLOBALS['show_as_php'])) { - $_message = __('Without PHP Code'); - } else { - $php_params['show_as_php'] = 1; - $_message = __('Create PHP Code'); - } - - $php_link = 'import.php' . PMA_generate_common_url($php_params); - $php_link = ' [' . PMA_linkOrButton($php_link, $_message) . ']'; - - if (isset($GLOBALS['show_as_php'])) { - $runquery_link = 'import.php' . PMA_generate_common_url($url_params); - $php_link .= ' [' - . PMA_linkOrButton($runquery_link, __('Submit Query')) . ']'; - } - } else { - $php_link = ''; - } //show as php - - // Refresh query - if (! empty($cfg['SQLQuery']['Refresh']) - && ! isset($GLOBALS['show_as_php']) // 'Submit query' does the same - && preg_match('@^(SELECT|SHOW)[[:space:]]+@i', $sql_query) - ) { - $refresh_link = 'import.php' . PMA_generate_common_url($url_params); - $refresh_link = ' [' - . PMA_linkOrButton($refresh_link, __('Refresh')) . ']'; - } else { - $refresh_link = ''; - } //refresh - - if (! empty($cfg['SQLValidator']['use']) - && ! empty($cfg['SQLQuery']['Validate']) - ) { - $validate_params = $url_params; - if (! empty($GLOBALS['validatequery'])) { - $validate_message = __('Skip Validate SQL'); - } else { - $validate_params['validatequery'] = 1; - $validate_message = __('Validate SQL'); - } - - $validate_link = 'import.php' - . PMA_generate_common_url($validate_params); - $validate_link = ' [' - . PMA_linkOrButton($validate_link, $validate_message) . ']'; - } else { - $validate_link = ''; - } //validator - - if (! empty($GLOBALS['validatequery'])) { - $retval .= '
'; - } else { - $retval .= ''; - } - if ($query_too_big) { - $retval .= $shortened_query_base; - } else { - $retval .= $query_base; - } - - //Clean up the end of the PHP - if (! empty($GLOBALS['show_as_php'])) { - $retval .= '";'; - } - if (! empty($GLOBALS['validatequery'])) { - $retval .= '
'; - } else { - $retval .= ''; - } - - $retval .= '
'; - // avoid displaying a Profiling checkbox that could - // be checked, which would reexecute an INSERT, for example - if (! empty($refresh_link)) { - $retval .= PMA_getProfilingForm($sql_query); - } - // if needed, generate an invisible form that contains controls for the - // Inline link; this way, the behavior of the Inline link does not - // depend on the profiling support or on the refresh link - if (empty($refresh_link) || ! PMA_profilingSupported()) { - $retval .= '
'; - $retval .= PMA_generate_common_hidden_inputs( - $GLOBALS['db'], $GLOBALS['table'] - ); - $retval .= ''; - $retval .= '
'; - } - - // in the tools div, only display the Inline link when not in ajax - // mode because 1) it currently does not work and 2) we would - // have two similar mechanisms on the page for the same goal - if ($is_select || ($GLOBALS['is_ajax_request'] === false) - && ! $query_too_big - ) { - // see in js/functions.js the jQuery code attached to id inline_edit - // document.write conflicts with jQuery, hence used $().append() - $retval .= ""; - } - $retval .= $edit_link . $explain_link . $php_link - . $refresh_link . $validate_link; - $retval .= '
'; - } - - $retval .= '
'; - if ($GLOBALS['is_ajax_request'] === false) { - $retval .= '
'; - } - - return $retval; - -} // end of the 'PMA_getMessage()' function - - -/** - * Verifies if current MySQL server supports profiling - * - * @access public - * - * @return boolean whether profiling is supported - */ -function PMA_profilingSupported() -{ - if (! PMA_cacheExists('profiling_supported', true)) { - // 5.0.37 has profiling but for example, 5.1.20 does not - // (avoid a trip to the server for MySQL before 5.0.37) - // and do not set a constant as we might be switching servers - if (defined('PMA_MYSQL_INT_VERSION') - && (PMA_MYSQL_INT_VERSION >= 50037) - && PMA_DBI_fetch_value("SHOW VARIABLES LIKE 'profiling'") - ) { - PMA_cacheSet('profiling_supported', true, true); - } else { - PMA_cacheSet('profiling_supported', false, true); - } - } - - return PMA_cacheGet('profiling_supported', true); -} - - -/** - * Returns HTML for the form with the Profiling checkbox - * - * @param string $sql_query sql query - * - * @return string HTML for the form with the Profiling checkbox - * - * @access public - */ -function PMA_getProfilingForm($sql_query) -{ - $retval = ''; - if (PMA_profilingSupported()) { - - $retval .= '
' . "\n"; - $retval .= PMA_generate_common_hidden_inputs( - $GLOBALS['db'], $GLOBALS['table'] - ); - - $retval .= '' . "\n" - . '' . "\n"; - - $retval .= PMA_getCheckbox( - 'profiling', __('Profiling'), isset($_SESSION['profiling']), true - ); - $retval .= '
' . "\n"; - - } - return $retval; -} - - -/** - * Formats $value to byte view - * - * @param double $value the value to format - * @param int $limes the sensitiveness - * @param int $comma the number of decimals to retain - * - * @return array the formatted value and its unit - * - * @access public - */ -function PMA_formatByteDown($value, $limes = 6, $comma = 0) -{ - if ($value === null) { - return null; - } - - $byteUnits = array( - /* l10n: shortcuts for Byte */ - __('B'), - /* l10n: shortcuts for Kilobyte */ - __('KiB'), - /* l10n: shortcuts for Megabyte */ - __('MiB'), - /* l10n: shortcuts for Gigabyte */ - __('GiB'), - /* l10n: shortcuts for Terabyte */ - __('TiB'), - /* l10n: shortcuts for Petabyte */ - __('PiB'), - /* l10n: shortcuts for Exabyte */ - __('EiB') - ); - - $dh = PMA_pow(10, $comma); - $li = PMA_pow(10, $limes); - $unit = $byteUnits[0]; - - for ($d = 6, $ex = 15; $d >= 1; $d--, $ex-=3) { - if (isset($byteUnits[$d]) && ($value >= $li * PMA_pow(10, $ex))) { - // use 1024.0 to avoid integer overflow on 64-bit machines - $value = round($value / (PMA_pow(1024, $d) / $dh)) /$dh; - $unit = $byteUnits[$d]; - break 1; - } // end if - } // end for - - if ($unit != $byteUnits[0]) { - // if the unit is not bytes (as represented in current language) - // reformat with max length of 5 - // 4th parameter=true means do not reformat if value < 1 - $return_value = PMA_formatNumber($value, 5, $comma, true); - } else { - // do not reformat, just handle the locale - $return_value = PMA_formatNumber($value, 0); - } - - return array(trim($return_value), $unit); -} // end of the 'PMA_formatByteDown' function - - -/** - * Changes thousands and decimal separators to locale specific values. - * - * @param string $value the value - * - * @return string - */ -function PMA_localizeNumber($value) -{ - return str_replace( - array(',', '.'), - array( - /* l10n: Thousands separator */ - __(','), - /* l10n: Decimal separator */ - __('.'), - ), - $value - ); -} - - -/** - * Formats $value to the given length and appends SI prefixes - * with a $length of 0 no truncation occurs, number is only formated - * to the current locale - * - * examples: - * - * echo PMA_formatNumber(123456789, 6); // 123,457 k - * echo PMA_formatNumber(-123456789, 4, 2); // -123.46 M - * echo PMA_formatNumber(-0.003, 6); // -3 m - * echo PMA_formatNumber(0.003, 3, 3); // 0.003 - * echo PMA_formatNumber(0.00003, 3, 2); // 0.03 m - * echo PMA_formatNumber(0, 6); // 0 - * - * - * @param double $value the value to format - * @param integer $digits_left number of digits left of the comma - * @param integer $digits_right number of digits right of the comma - * @param boolean $only_down do not reformat numbers below 1 - * @param boolean $noTrailingZero removes trailing zeros right of the comma - * (default: true) - * - * @return string the formatted value and its unit - * - * @access public - */ -function PMA_formatNumber( - $value, $digits_left = 3, $digits_right = 0, - $only_down = false, $noTrailingZero = true -) { - - if ($value == 0) { - return '0'; - } - - $originalValue = $value; - //number_format is not multibyte safe, str_replace is safe - if ($digits_left === 0) { - $value = number_format($value, $digits_right); - if (($originalValue != 0) && (floatval($value) == 0)) { - $value = ' <' . (1 / PMA_pow(10, $digits_right)); - } - - return PMA_localizeNumber($value); - } - - // this units needs no translation, ISO - $units = array( - -8 => 'y', - -7 => 'z', - -6 => 'a', - -5 => 'f', - -4 => 'p', - -3 => 'n', - -2 => 'µ', - -1 => 'm', - 0 => ' ', - 1 => 'k', - 2 => 'M', - 3 => 'G', - 4 => 'T', - 5 => 'P', - 6 => 'E', - 7 => 'Z', - 8 => 'Y' - ); - - // check for negative value to retain sign - if ($value < 0) { - $sign = '-'; - $value = abs($value); - } else { - $sign = ''; - } - - $dh = PMA_pow(10, $digits_right); - - /* - * This gives us the right SI prefix already, - * but $digits_left parameter not incorporated + /** + * Displays a link to the phpMyAdmin documentation + * + * @param string $anchor anchor in documentation + * + * @return string the html link + * + * @access public */ - $d = floor(log10($value) / 3); - /* - * Lowering the SI prefix by 1 gives us an additional 3 zeros - * So if we have 3,6,9,12.. free digits ($digits_left - $cur_digits) - * to use, then lower the SI prefix + public function PMA_showDocu($anchor) + { + return $this->PMA_showDocLink('Documentation.html#' . $anchor); + } // end of the 'PMA_showDocu()' function + + + /** + * Displays a link to the PHP documentation + * + * @param string $target anchor in documentation + * + * @return string the html link + * + * @access public */ - $cur_digits = floor(log10($value / PMA_pow(1000, $d, 'pow'))+1); - if ($digits_left > $cur_digits) { - $d -= floor(($digits_left - $cur_digits)/3); - } + public function PMA_showPHPDocu($target) + { + $url = PMA_getPHPDocLink($target); - if ($d < 0 && $only_down) { - $d = 0; - } - - $value = round($value / (PMA_pow(1000, $d, 'pow') / $dh)) /$dh; - $unit = $units[$d]; - - // If we dont want any zeros after the comma just add the thousand separator - if ($noTrailingZero) { - $value = PMA_localizeNumber( - preg_replace('/(?<=\d)(?=(\d{3})+(?!\d))/', ',', $value) - ); - } else { - //number_format is not multibyte safe, str_replace is safe - $value = PMA_localizeNumber(number_format($value, $digits_right)); - } - - if ($originalValue != 0 && floatval($value) == 0) { - return ' <' . (1 / PMA_pow(10, $digits_right)) . ' ' . $unit; - } - - return $sign . $value . ' ' . $unit; -} // end of the 'PMA_formatNumber' function + return $this->PMA_showDocLink($url); + } // end of the 'PMA_showPHPDocu()' function -/** - * Returns the number of bytes when a formatted size is given - * - * @param string $formatted_size the size expression (for example 8MB) - * - * @return integer The numerical part of the expression (for example 8) - */ -function PMA_extractValueFromFormattedSize($formatted_size) -{ - $return_value = -1; - - if (preg_match('/^[0-9]+GB$/', $formatted_size)) { - $return_value = substr($formatted_size, 0, -2) * PMA_pow(1024, 3); - } elseif (preg_match('/^[0-9]+MB$/', $formatted_size)) { - $return_value = substr($formatted_size, 0, -2) * PMA_pow(1024, 2); - } elseif (preg_match('/^[0-9]+K$/', $formatted_size)) { - $return_value = substr($formatted_size, 0, -1) * PMA_pow(1024, 1); - } - return $return_value; -}// end of the 'PMA_extractValueFromFormattedSize' function - - -/** - * Writes localised date - * - * @param string $timestamp the current timestamp - * @param string $format format - * - * @return string the formatted date - * - * @access public - */ -function PMA_localisedDate($timestamp = -1, $format = '') -{ - $month = array( - /* l10n: Short month name */ - __('Jan'), - /* l10n: Short month name */ - __('Feb'), - /* l10n: Short month name */ - __('Mar'), - /* l10n: Short month name */ - __('Apr'), - /* l10n: Short month name */ - _pgettext('Short month name', 'May'), - /* l10n: Short month name */ - __('Jun'), - /* l10n: Short month name */ - __('Jul'), - /* l10n: Short month name */ - __('Aug'), - /* l10n: Short month name */ - __('Sep'), - /* l10n: Short month name */ - __('Oct'), - /* l10n: Short month name */ - __('Nov'), - /* l10n: Short month name */ - __('Dec')); - $day_of_week = array( - /* l10n: Short week day name */ - _pgettext('Short week day name', 'Sun'), - /* l10n: Short week day name */ - __('Mon'), - /* l10n: Short week day name */ - __('Tue'), - /* l10n: Short week day name */ - __('Wed'), - /* l10n: Short week day name */ - __('Thu'), - /* l10n: Short week day name */ - __('Fri'), - /* l10n: Short week day name */ - __('Sat')); - - if ($format == '') { - /* l10n: See http://www.php.net/manual/en/function.strftime.php */ - $format = __('%B %d, %Y at %I:%M %p'); - } - - if ($timestamp == -1) { - $timestamp = time(); - } - - $date = preg_replace( - '@%[aA]@', - $day_of_week[(int)strftime('%w', $timestamp)], - $format - ); - $date = preg_replace( - '@%[bB]@', - $month[(int)strftime('%m', $timestamp)-1], - $date - ); - - return strftime($date, $timestamp); -} // end of the 'PMA_localisedDate()' function - - -/** - * returns a tab for tabbed navigation. - * If the variables $link and $args ar left empty, an inactive tab is created - * - * @param array $tab array with all options - * @param array $url_params tab specific URL parameters - * - * @return string html code for one tab, a link if valid otherwise a span - * - * @access public - */ -function PMA_generateHtmlTab($tab, $url_params = array()) -{ - // default values - $defaults = array( - 'text' => '', - 'class' => '', - 'active' => null, - 'link' => '', - 'sep' => '?', - 'attr' => '', - 'args' => '', - 'warning' => '', - 'fragment' => '', - 'id' => '', - ); - - $tab = array_merge($defaults, $tab); - - // determine additionnal style-class - if (empty($tab['class'])) { - if (! empty($tab['active']) - || PMA_isValid($GLOBALS['active_page'], 'identical', $tab['link']) - ) { - $tab['class'] = 'active'; - } elseif (is_null($tab['active']) && empty($GLOBALS['active_page']) - && (basename($GLOBALS['PMA_PHP_SELF']) == $tab['link']) - && empty($tab['warning'])) { - $tab['class'] = 'active'; - } - } - - if (! empty($tab['warning'])) { - $tab['class'] .= ' error'; - $tab['attr'] .= ' title="' . htmlspecialchars($tab['warning']) . '"'; - } - - // If there are any tab specific URL parameters, merge those with - // the general URL parameters - if (! empty($tab['url_params']) && is_array($tab['url_params'])) { - $url_params = array_merge($url_params, $tab['url_params']); - } - - // build the link - if (! empty($tab['link'])) { - $tab['link'] = htmlentities($tab['link']); - $tab['link'] = $tab['link'] . PMA_generate_common_url($url_params); - if (! empty($tab['args'])) { - foreach ($tab['args'] as $param => $value) { - $tab['link'] .= PMA_get_arg_separator('html') . urlencode($param) - . '=' . urlencode($value); - } - } - } - - if (! empty($tab['fragment'])) { - $tab['link'] .= $tab['fragment']; - } - - // display icon - if (isset($tab['icon']) - ) { - // avoid generating an alt tag, because it only illustrates - // the text that follows and if browser does not display - // images, the text is duplicated - $tab['text'] = PMA_getImage(htmlentities($tab['icon'])) . $tab['text']; - - } elseif (empty($tab['text'])) { - // check to not display an empty link-text - $tab['text'] = '?'; - trigger_error( - 'empty linktext in function ' . __FUNCTION__ . '()', - E_USER_NOTICE - ); - } - - //Set the id for the tab, if set in the params - $id_string = ( empty($tab['id']) ? '' : ' id="'.$tab['id'].'" ' ); - $out = ''; - - if (! empty($tab['link'])) { - $out .= '' - . $tab['text'] . ''; - } else { - $out .= '' - . $tab['text'] . ''; - } - - $out .= ''; - return $out; -} // end of the 'PMA_generateHtmlTab()' function - - -/** - * returns html-code for a tab navigation - * - * @param array $tabs one element per tab - * @param string $url_params additional URL parameters - * @param string $menu_id HTML id attribute for the menu container - * - * @return string html-code for tab-navigation - */ -function PMA_generateHtmlTabs($tabs, $url_params, $menu_id = 'topmenu') -{ - $tab_navigation = '' . "\n"; - - return $tab_navigation; -} - - -/** - * Displays a link, or a button if the link's URL is too large, to - * accommodate some browsers' limitations - * - * @param string $url the URL - * @param string $message the link message - * @param mixed $tag_params string: js confirmation - * array: additional tag params (f.e. style="") - * @param boolean $new_form we set this to false when we are already in - * a form, to avoid generating nested forms - * @param boolean $strip_img whether to strip the image - * @param string $target target - * - * @return string the results to be echoed or saved in an array - */ -function PMA_linkOrButton( - $url, $message, $tag_params = array(), - $new_form = true, $strip_img = false, $target = '' -) { - - $url_length = strlen($url); - // with this we should be able to catch case of image upload - // into a (MEDIUM) BLOB; not worth generating even a form for these - if ($url_length > $GLOBALS['cfg']['LinkLengthLimit'] * 100) { - return ''; - } - - - if (! is_array($tag_params)) { - $tmp = $tag_params; - $tag_params = array(); - if (! empty($tmp)) { - $tag_params['onclick'] = 'return confirmLink(this, \'' - . PMA_escapeJsString($tmp) . '\')'; - } - unset($tmp); - } - if (! empty($target)) { - $tag_params['target'] = htmlentities($target); - } - - $tag_params_strings = array(); - foreach ($tag_params as $par_name => $par_value) { - // htmlspecialchars() only on non javascript - $par_value = substr($par_name, 0, 2) == 'on' - ? $par_value - : htmlspecialchars($par_value); - $tag_params_strings[] = $par_name . '="' . $par_value . '"'; - } - - $displayed_message = ''; - // Add text if not already added - if (stristr($message, '' + . $this->PMA_getImage('b_help.png') + . '' . $message . '' . ''; } - // Suhosin: Check that each query parameter is not above maximum - $in_suhosin_limits = true; - if ($url_length <= $GLOBALS['cfg']['LinkLengthLimit']) { - if ($suhosin_get_MaxValueLength = ini_get('suhosin.get.max_value_length')) { - $query_parts = PMA_splitURLQuery($url); - foreach ($query_parts as $query_pair) { - list($eachvar, $eachval) = explode('=', $query_pair); - if (strlen($eachval) > $suhosin_get_MaxValueLength) { - $in_suhosin_limits = false; - break; + + /** + * Displays a MySQL error message in the right frame. + * + * @param string $error_message the error message + * @param string $the_query the sql query that failed + * @param bool $is_modify_link whether to show a "modify" link or not + * @param string $back_url the "back" link url (full path is not required) + * @param bool $exit EXIT the page? + * + * @return void + * + * @global string the curent table + * @global string the current db + * + * @access public + */ + public function PMA_mysqlDie( + $error_message = '', $the_query = '', + $is_modify_link = true, $back_url = '', $exit = true + ) { + + global $table, $db; + + $error_msg = ''; + + if (! $error_message) { + $error_message = PMA_DBI_getError(); + } + if (! $the_query && ! empty($GLOBALS['sql_query'])) { + $the_query = $GLOBALS['sql_query']; + } + + // --- Added to solve bug #641765 + if (! function_exists('PMA_SQP_isError') || PMA_SQP_isError()) { + $formatted_sql = htmlspecialchars($the_query); + } elseif (empty($the_query) || (trim($the_query) == '')) { + $formatted_sql = ''; + } else { + if (strlen($the_query) > $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']) { + $formatted_sql = htmlspecialchars( + substr($the_query, 0, $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']) + ) + . '[...]'; + } else { + $formatted_sql = $this->PMA_formatSql(PMA_SQP_parse($the_query), $the_query); + } + } + // --- + $error_msg .= "\n" . '' . "\n"; + $error_msg .= '

' . __('Error') + . '

' . "\n"; + // if the config password is wrong, or the MySQL server does not + // respond, do not show the query that would reveal the + // username/password + if (! empty($the_query) && ! strstr($the_query, 'connect')) { + // --- Added to solve bug #641765 + if (function_exists('PMA_SQP_isError') && PMA_SQP_isError()) { + $error_msg .= PMA_SQP_getErrorString() . "\n"; + $error_msg .= '
' . "\n"; + } + // --- + // modified to show the help on sql errors + $error_msg .= '

' . __('SQL query') . ':' . "\n"; + if (strstr(strtolower($formatted_sql), 'select')) { + // please show me help to the error on select + $error_msg .= $this->PMA_showMySQLDocu('SQL-Syntax', 'SELECT'); + } + if ($is_modify_link) { + $_url_params = array( + 'sql_query' => $the_query, + 'show_query' => 1, + ); + if (strlen($table)) { + $_url_params['db'] = $db; + $_url_params['table'] = $table; + $doedit_goto = ''; + } elseif (strlen($db)) { + $_url_params['db'] = $db; + $doedit_goto = ''; + } else { + $doedit_goto = ''; + } + + $error_msg .= $doedit_goto + . $this->PMA_getIcon('b_edit.png', __('Edit')) + . ''; + } // end if + $error_msg .= '

' . "\n" + .'

' . "\n" + . $formatted_sql . "\n" + . '

' . "\n"; + } // end if + + if (! empty($error_message)) { + $error_message = preg_replace( + "@((\015\012)|(\015)|(\012)){3,}@", + "\n\n", + $error_message + ); + } + // modified to show the help on error-returns + // (now error-messages-server) + $error_msg .= '

' . "\n" + . ' ' . __('MySQL said: ') . '' + . $this->PMA_showMySQLDocu('Error-messages-server', 'Error-messages-server') + . "\n" + . '

' . "\n"; + + // The error message will be displayed within a CODE segment. + // To preserve original formatting, but allow wordwrapping, + // we do a couple of replacements + + // Replace all non-single blanks with their HTML-counterpart + $error_message = str_replace(' ', '  ', $error_message); + // Replace TAB-characters with their HTML-counterpart + $error_message = str_replace("\t", '    ', $error_message); + // Replace linebreaks + $error_message = nl2br($error_message); + + $error_msg .= '' . "\n" + . $error_message . "\n" + . '
' . "\n"; + $error_msg .= '
'; + + $_SESSION['Import_message']['message'] = $error_msg; + + if ($exit) { + /** + * If in an Ajax request + * - avoid displaying a Back link + * - use PMA_Response() to transmit the message and exit + */ + if ($GLOBALS['is_ajax_request'] == true) { + $response = PMA_Response::getInstance(); + $response->isSuccess(false); + $response->addJSON('message', $error_msg); + exit; + } + if (! empty($back_url)) { + if (strstr($back_url, '?')) { + $back_url .= '&no_history=true'; + } else { + $back_url .= '?no_history=true'; + } + + $_SESSION['Import_message']['go_back_url'] = $back_url; + + $error_msg .= '
'; + $error_msg .= '[ ' . __('Back') . ' ]'; + $error_msg .= '
' . "\n\n"; + } + + echo $error_msg; + exit; + } else { + echo $error_msg; + } + } // end of the 'PMA_mysqlDie()' function + + + /** + * returns array with tables of given db with extended information and grouped + * + * @param string $db name of db + * @param string $tables name of tables + * @param integer $limit_offset list offset + * @param int|bool $limit_count max tables to return + * + * @return array (recursive) grouped table list + */ + public function PMA_getTableList($db, $tables = null, $limit_offset = 0, + $limit_count = false + ) { + $sep = $GLOBALS['cfg']['LeftFrameTableSeparator']; + + if ($tables === null) { + $tables = PMA_DBI_get_tables_full( + $db, false, false, null, $limit_offset, $limit_count + ); + if ($GLOBALS['cfg']['NaturalOrder']) { + uksort($tables, 'strnatcasecmp'); + } + } + + if (count($tables) < 1) { + return $tables; + } + + $default = array( + 'Name' => '', + 'Rows' => 0, + 'Comment' => '', + 'disp_name' => '', + ); + + $table_groups = array(); + + foreach ($tables as $table_name => $table) { + // check for correct row count + if ($table['Rows'] === null) { + // Do not check exact row count here, + // if row count is invalid possibly the table is defect + // and this would break left frame; + // but we can check row count if this is a view or the + // information_schema database + // since PMA_Table::countRecords() returns a limited row count + // in this case. + + // set this because PMA_Table::countRecords() can use it + $tbl_is_view = $table['TABLE_TYPE'] == 'VIEW'; + + if ($tbl_is_view || PMA_is_system_schema($db)) { + $table['Rows'] = PMA_Table::countRecords( + $db, + $table['Name'], + false, + true + ); } } - } - } - if (($url_length <= $GLOBALS['cfg']['LinkLengthLimit']) && $in_suhosin_limits) { - - // no whitespace within an else Safari will make it part of the link - $ret = "\n" . '' - . $message . $displayed_message . '' . "\n"; - - } else { - - // no spaces (linebreaks) at all - // or after the hidden fields - // IE will display them all - - // add class=link to submit button - if (empty($tag_params['class'])) { - $tag_params['class'] = 'link'; - } - - if (! isset($query_parts)) { - $query_parts = PMA_splitURLQuery($url); - } - $url_parts = parse_url($url); - - if ($new_form) { - $ret = ''; - } - } // end if... else... - - return $ret; -} // end of the 'PMA_linkOrButton()' function - - -/** - * Splits a URL string by parameter - * - * @param string $url the URL - * - * @return array the parameter/value pairs, for example [0] db=sakila - */ -function PMA_splitURLQuery($url) -{ - // decode encoded url separators - $separator = PMA_get_arg_separator(); - // on most places separator is still hard coded ... - if ($separator !== '&') { - // ... so always replace & with $separator - $url = str_replace(htmlentities('&'), $separator, $url); - $url = str_replace('&', $separator, $url); - } - - $url = str_replace(htmlentities($separator), $separator, $url); - // end decode - - $url_parts = parse_url($url); - - return explode($separator, $url_parts['query']); -} - - -/** - * Returns a given timespan value in a readable format. - * - * @param int $seconds the timespan - * - * @return string the formatted value - */ -function PMA_timespanFormat($seconds) -{ - $days = floor($seconds / 86400); - if ($days > 0) { - $seconds -= $days * 86400; - } - - $hours = floor($seconds / 3600); - if ($days > 0 || $hours > 0) { - $seconds -= $hours * 3600; - } - - $minutes = floor($seconds / 60); - if ($days > 0 || $hours > 0 || $minutes > 0) { - $seconds -= $minutes * 60; - } - - return sprintf( - __('%s days, %s hours, %s minutes and %s seconds'), - (string)$days, (string)$hours, (string)$minutes, (string)$seconds - ); -} - - -/** - * Takes a string and outputs each character on a line for itself. Used - * mainly for horizontalflipped display mode. - * Takes care of special html-characters. - * Fulfills todo-item - * http://sf.net/tracker/?func=detail&aid=544361&group_id=23067&atid=377411 - * - * @param string $string The string - * @param string $Separator The Separator (defaults to "
\n") - * - * @access public - * @todo add a multibyte safe function PMA_STR_split() - * - * @return string The flipped string - */ -function PMA_flipstring($string, $Separator = "
\n") -{ - $format_string = ''; - $charbuff = false; - - for ($i = 0, $str_len = strlen($string); $i < $str_len; $i++) { - $char = $string{$i}; - $append = false; - - if ($char == '&') { - $format_string .= $charbuff; - $charbuff = $char; - } elseif ($char == ';' && ! empty($charbuff)) { - $format_string .= $charbuff . $char; - $charbuff = false; - $append = true; - } elseif (! empty($charbuff)) { - $charbuff .= $char; - } else { - $format_string .= $char; - $append = true; - } - - // do not add separator after the last character - if ($append && ($i != $str_len - 1)) { - $format_string .= $Separator; - } - } - - return $format_string; -} - - -/** - * Function added to avoid path disclosures. - * Called by each script that needs parameters, it displays - * an error message and, by default, stops the execution. - * - * Not sure we could use a strMissingParameter message here, - * would have to check if the error message file is always available - * - * @param array $params The names of the parameters needed by the calling script. - * @param bool $request Whether to include this list in checking for special params. - * - * @return void - * - * @global string path to current script - * @global boolean flag whether any special variable was required - * - * @access public - */ -function PMA_checkParameters($params, $request = true) -{ - global $checked_special; - - if (! isset($checked_special)) { - $checked_special = false; - } - - $reported_script_name = basename($GLOBALS['PMA_PHP_SELF']); - $found_error = false; - $error_message = ''; - - foreach ($params as $param) { - if ($request && ($param != 'db') && ($param != 'table')) { - $checked_special = true; - } - - if (! isset($GLOBALS[$param])) { - $error_message .= $reported_script_name - . ': ' . __('Missing parameter:') . ' ' - . $param - . PMA_showDocu('faqmissingparameters') - . '
'; - $found_error = true; - } - } - if ($found_error) { - PMA_fatalError($error_message, null, false); - } -} // end function - - -/** - * Function to generate unique condition for specified row. - * - * @param resource $handle current query result - * @param integer $fields_cnt number of fields - * @param array $fields_meta meta information about fields - * @param array $row current row - * @param boolean $force_unique generate condition only on pk or unique - * - * @access public - * - * @return array the calculated condition and whether condition is unique - */ -function PMA_getUniqueCondition($handle, $fields_cnt, $fields_meta, $row, - $force_unique = false -) { - - $primary_key = ''; - $unique_key = ''; - $nonprimary_condition = ''; - $preferred_condition = ''; - $primary_key_array = array(); - $unique_key_array = array(); - $nonprimary_condition_array = array(); - $condition_array = array(); - - for ($i = 0; $i < $fields_cnt; ++$i) { - - $condition = ''; - $con_key = ''; - $con_val = ''; - $field_flags = PMA_DBI_field_flags($handle, $i); - $meta = $fields_meta[$i]; - - // do not use a column alias in a condition - if (! isset($meta->orgname) || ! strlen($meta->orgname)) { - $meta->orgname = $meta->name; - - if (isset($GLOBALS['analyzed_sql'][0]['select_expr']) - && is_array($GLOBALS['analyzed_sql'][0]['select_expr']) + // in $group we save the reference to the place in $table_groups + // where to store the table info + if ($GLOBALS['cfg']['LeftFrameDBTree'] + && $sep && strstr($table_name, $sep) ) { - foreach ( - $GLOBALS['analyzed_sql'][0]['select_expr'] as $select_expr + + $parts = explode($sep, $table_name); + + $group =& $table_groups; + $i = 0; + $group_name_full = ''; + $parts_cnt = count($parts) - 1; + + while (($i < $parts_cnt) + && ($i < $GLOBALS['cfg']['LeftFrameTableLevel']) ) { - // need (string) === (string) - // '' !== 0 but '' == 0 - if ((string) $select_expr['alias'] === (string) $meta->name) { - $meta->orgname = $select_expr['column']; - break; - } // end if - } // end foreach - } - } - // Do not use a table alias in a condition. - // Test case is: - // select * from galerie x WHERE - //(select count(*) from galerie y where y.datum=x.datum)>1 - // - // But orgtable is present only with mysqli extension so the - // fix is only for mysqli. - // Also, do not use the original table name if we are dealing with - // a view because this view might be updatable. - // (The isView() verification should not be costly in most cases - // because there is some caching in the function). - if (isset($meta->orgtable) - && ($meta->table != $meta->orgtable) - && ! PMA_Table::isView($GLOBALS['db'], $meta->table) - ) { - $meta->table = $meta->orgtable; - } + $group_name = $parts[$i] . $sep; + $group_name_full .= $group_name; - // to fix the bug where float fields (primary or not) - // can't be matched because of the imprecision of - // floating comparison, use CONCAT - // (also, the syntax "CONCAT(field) IS NULL" - // that we need on the next "if" will work) - if ($meta->type == 'real') { - $con_key = 'CONCAT(' . PMA_backquote($meta->table) . '.' - . PMA_backquote($meta->orgname) . ')'; - } else { - $con_key = PMA_backquote($meta->table) . '.' - . PMA_backquote($meta->orgname); - } // end if... else... - $condition = ' ' . $con_key . ' '; + if (! isset($group[$group_name])) { - if (! isset($row[$i]) || is_null($row[$i])) { - $con_val = 'IS NULL'; - } else { - - // timestamp is numeric on some MySQL 4.1 - // for real we use CONCAT above and it should compare to string - if ($meta->numeric - && ($meta->type != 'timestamp') - && ($meta->type != 'real') - ) { - - $con_val = '= ' . $row[$i]; - - } elseif ((($meta->type == 'blob') || ($meta->type == 'string')) - // hexify only if this is a true not empty BLOB or a BINARY - && stristr($field_flags, 'BINARY') - && ! empty($row[$i]) - ) { - - // do not waste memory building a too big condition - if (strlen($row[$i]) < 1000) { - // use a CAST if possible, to avoid problems - // if the field contains wildcard characters % or _ - $con_val = '= CAST(0x' . bin2hex($row[$i]) . ' AS BINARY)'; - } else { - // this blob won't be part of the final condition - $con_val = null; + $group[$group_name] = array(); + $group[$group_name]['is' . $sep . 'group'] = true; + $group[$group_name]['tab' . $sep . 'count'] = 1; + $group[$group_name]['tab' . $sep . 'group'] = $group_name_full; + + } elseif (! isset($group[$group_name]['is' . $sep . 'group'])) { + + $table = $group[$group_name]; + $group[$group_name] = array(); + $group[$group_name][$group_name] = $table; + unset($table); + $group[$group_name]['is' . $sep . 'group'] = true; + $group[$group_name]['tab' . $sep . 'count'] = 1; + $group[$group_name]['tab' . $sep . 'group'] = $group_name_full; + + } else { + $group[$group_name]['tab' . $sep . 'count']++; + } + + $group =& $group[$group_name]; + $i++; } - - } elseif (in_array($meta->type, PMA_getGISDatatypes()) - && ! empty($row[$i]) - ) { - - // do not build a too big condition - if (strlen($row[$i]) < 5000) { - $condition .= '=0x' . bin2hex($row[$i]) . ' AND'; - } else { - $condition = ''; - } - - } elseif ($meta->type == 'bit') { - - $con_val = "= b'" - . PMA_printableBitValue($row[$i], $meta->length) . "'"; - + } else { - $con_val = '= \'' . PMA_sqlAddSlashes($row[$i], false, true) . '\''; - } - } - - if ($con_val != null) { - - $condition .= $con_val . ' AND'; - - if ($meta->primary_key > 0) { - $primary_key .= $condition; - $primary_key_array[$con_key] = $con_val; - } elseif ($meta->unique_key > 0) { - $unique_key .= $condition; - $unique_key_array[$con_key] = $con_val; + if (! isset($table_groups[$table_name])) { + $table_groups[$table_name] = array(); + } + $group =& $table_groups; } - - $nonprimary_condition .= $condition; - $nonprimary_condition_array[$con_key] = $con_val; - } - } // end for - - // Correction University of Virginia 19991216: - // prefer primary or unique keys for condition, - // but use conjunction of all values if no primary key - $clause_is_unique = true; - - if ($primary_key) { - $preferred_condition = $primary_key; - $condition_array = $primary_key_array; - - } elseif ($unique_key) { - $preferred_condition = $unique_key; - $condition_array = $unique_key_array; - - } elseif (! $force_unique) { - $preferred_condition = $nonprimary_condition; - $condition_array = $nonprimary_condition_array; - $clause_is_unique = false; - } - - $where_clause = trim(preg_replace('|\s?AND$|', '', $preferred_condition)); - return(array($where_clause, $clause_is_unique, $condition_array)); -} // end function -/** - * Generate a button or image tag - * - * @param string $button_name name of button element - * @param string $button_class class of button element - * @param string $image_name name of image element - * @param string $text text to display - * @param string $image image to display - * @param string $value value - * - * @return string html content - * - * @access public - */ -function PMA_getButtonOrImage( - $button_name, $button_class, $image_name, $text, $image, $value = '' -) { - - if ($value == '') { - $value = $text; - } - - if ($GLOBALS['cfg']['PropertiesIconic'] === false) { - return ' ' . "\n"; - } - - /* Opera has trouble with */ - /* IE has trouble with ' . "\n"; - } else { - return '' - . ($GLOBALS['cfg']['PropertiesIconic'] === 'both' - ? ' ' . htmlspecialchars($text) - : '') . "\n"; - } -} // end function - - -/** - * Generate a pagination selector for browsing resultsets - * - * @param int $rows Number of rows in the pagination set - * @param int $pageNow current page number - * @param int $nbTotalPage number of total pages - * @param int $showAll If the number of pages is lower than this - * variable, no pages will be omitted in pagination - * @param int $sliceStart How many rows at the beginning should always be shown? - * @param int $sliceEnd How many rows at the end should always be shown? - * @param int $percent Percentage of calculation page offsets to hop to a - * next page - * @param int $range Near the current page, how many pages should - * be considered "nearby" and displayed as well? - * @param string $prompt The prompt to display (sometimes empty) - * - * @return string - * - * @access public - */ -function PMA_pageselector( - $rows, $pageNow = 1, $nbTotalPage = 1, $showAll = 200, $sliceStart = 5, - $sliceEnd = 5, $percent = 20, $range = 10, $prompt = '' -) { - - $increment = floor($nbTotalPage / $percent); - $pageNowMinusRange = ($pageNow - $range); - $pageNowPlusRange = ($pageNow + $range); - - $gotopage = $prompt . ' '; + $retval .= ''; + } + + // in the tools div, only display the Inline link when not in ajax + // mode because 1) it currently does not work and 2) we would + // have two similar mechanisms on the page for the same goal + if ($is_select || ($GLOBALS['is_ajax_request'] === false) + && ! $query_too_big + ) { + // see in js/functions.js the jQuery code attached to id inline_edit + // document.write conflicts with jQuery, hence used $().append() + $retval .= ""; + } + $retval .= $edit_link . $explain_link . $php_link + . $refresh_link . $validate_link; + $retval .= ''; + } + + $retval .= ''; + if ($GLOBALS['is_ajax_request'] === false) { + $retval .= '
'; + } + + return $retval; + + } // end of the 'PMA_getMessage()' function + + + /** + * Verifies if current MySQL server supports profiling + * + * @access public + * + * @return boolean whether profiling is supported + */ + public function PMA_profilingSupported() + { + if (!$this->PMA_cacheExists('profiling_supported', true)) { + // 5.0.37 has profiling but for example, 5.1.20 does not + // (avoid a trip to the server for MySQL before 5.0.37) + // and do not set a constant as we might be switching servers + if (defined('PMA_MYSQL_INT_VERSION') + && (PMA_MYSQL_INT_VERSION >= 50037) + && PMA_DBI_fetch_value("SHOW VARIABLES LIKE 'profiling'") + ) { + $this->PMA_cacheSet('profiling_supported', true, true); + } else { + $this->PMA_cacheSet('profiling_supported', false, true); } } + return $this->PMA_cacheGet('profiling_supported', true); + } + + + /** + * Returns HTML for the form with the Profiling checkbox + * + * @param string $sql_query sql query + * + * @return string HTML for the form with the Profiling checkbox + * + * @access public + */ + public function PMA_getProfilingForm($sql_query) + { + $retval = ''; + if ($this->PMA_profilingSupported()) { + + $retval .= '
' . "\n"; + $retval .= PMA_generate_common_hidden_inputs( + $GLOBALS['db'], $GLOBALS['table'] + ); + + $retval .= '' . "\n" + . '' . "\n"; + + $retval .= $this->PMA_getCheckbox( + 'profiling', __('Profiling'), isset($_SESSION['profiling']), true + ); + $retval .= '
' . "\n"; + + } + return $retval; + } + + + /** + * Formats $value to byte view + * + * @param double $value the value to format + * @param int $limes the sensitiveness + * @param int $comma the number of decimals to retain + * + * @return array the formatted value and its unit + * + * @access public + */ + public function PMA_formatByteDown($value, $limes = 6, $comma = 0) + { + if ($value === null) { + return null; + } + + $byteUnits = array( + /* l10n: shortcuts for Byte */ + __('B'), + /* l10n: shortcuts for Kilobyte */ + __('KiB'), + /* l10n: shortcuts for Megabyte */ + __('MiB'), + /* l10n: shortcuts for Gigabyte */ + __('GiB'), + /* l10n: shortcuts for Terabyte */ + __('TiB'), + /* l10n: shortcuts for Petabyte */ + __('PiB'), + /* l10n: shortcuts for Exabyte */ + __('EiB') + ); + + $dh = $this->PMA_pow(10, $comma); + $li = $this->PMA_pow(10, $limes); + $unit = $byteUnits[0]; + + for ($d = 6, $ex = 15; $d >= 1; $d--, $ex-=3) { + if (isset($byteUnits[$d]) && ($value >= $li * $this->PMA_pow(10, $ex))) { + // use 1024.0 to avoid integer overflow on 64-bit machines + $value = round($value / ($this->PMA_pow(1024, $d) / $dh)) /$dh; + $unit = $byteUnits[$d]; + break 1; + } // end if + } // end for + + if ($unit != $byteUnits[0]) { + // if the unit is not bytes (as represented in current language) + // reformat with max length of 5 + // 4th parameter=true means do not reformat if value < 1 + $return_value = $this->PMA_formatNumber($value, 5, $comma, true); + } else { + // do not reformat, just handle the locale + $return_value = $this->PMA_formatNumber($value, 0); + } + + return array(trim($return_value), $unit); + } // end of the 'PMA_formatByteDown' function + + + /** + * Changes thousands and decimal separators to locale specific values. + * + * @param string $value the value + * + * @return string + */ + public function PMA_localizeNumber($value) + { + return str_replace( + array(',', '.'), + array( + /* l10n: Thousands separator */ + __(','), + /* l10n: Decimal separator */ + __('.'), + ), + $value + ); + } + + + /** + * Formats $value to the given length and appends SI prefixes + * with a $length of 0 no truncation occurs, number is only formated + * to the current locale + * + * examples: + * + * echo PMA_formatNumber(123456789, 6); // 123,457 k + * echo PMA_formatNumber(-123456789, 4, 2); // -123.46 M + * echo PMA_formatNumber(-0.003, 6); // -3 m + * echo PMA_formatNumber(0.003, 3, 3); // 0.003 + * echo PMA_formatNumber(0.00003, 3, 2); // 0.03 m + * echo PMA_formatNumber(0, 6); // 0 + * + * + * @param double $value the value to format + * @param integer $digits_left number of digits left of the comma + * @param integer $digits_right number of digits right of the comma + * @param boolean $only_down do not reformat numbers below 1 + * @param boolean $noTrailingZero removes trailing zeros right of the comma + * (default: true) + * + * @return string the formatted value and its unit + * + * @access public + */ + public function PMA_formatNumber( + $value, $digits_left = 3, $digits_right = 0, + $only_down = false, $noTrailingZero = true + ) { + + if ($value == 0) { + return '0'; + } + + $originalValue = $value; + //number_format is not multibyte safe, str_replace is safe + if ($digits_left === 0) { + $value = number_format($value, $digits_right); + if (($originalValue != 0) && (floatval($value) == 0)) { + $value = ' <' . (1 / $this->PMA_pow(10, $digits_right)); + } + + return $this->PMA_localizeNumber($value); + } + + // this units needs no translation, ISO + $units = array( + -8 => 'y', + -7 => 'z', + -6 => 'a', + -5 => 'f', + -4 => 'p', + -3 => 'n', + -2 => 'µ', + -1 => 'm', + 0 => ' ', + 1 => 'k', + 2 => 'M', + 3 => 'G', + 4 => 'T', + 5 => 'P', + 6 => 'E', + 7 => 'Z', + 8 => 'Y' + ); + + // check for negative value to retain sign + if ($value < 0) { + $sign = '-'; + $value = abs($value); + } else { + $sign = ''; + } + + $dh = $this->PMA_pow(10, $digits_right); + /* - Add page numbers with "geometrically increasing" distances. - - This helps me a lot when navigating through giant tables. - - Test case: table with 2.28 million sets, 76190 pages. Page of interest - is between 72376 and 76190. - Selecting page 72376. - Now, old version enumerated only +/- 10 pages around 72376 and the - percentage increment produced steps of about 3000. - - The following code adds page numbers +/- 2,4,8,16,32,64,128,256 etc. - around the current page. - */ - - $i = $pageNow; - $dist = 1; - while ($i < $x) { - $dist = 2 * $dist; - $i = $pageNow + $dist; - if ($i > 0 && $i <= $x) { - $pages[] = $i; - } + * This gives us the right SI prefix already, + * but $digits_left parameter not incorporated + */ + $d = floor(log10($value) / 3); + /* + * Lowering the SI prefix by 1 gives us an additional 3 zeros + * So if we have 3,6,9,12.. free digits ($digits_left - $cur_digits) + * to use, then lower the SI prefix + */ + $cur_digits = floor(log10($value / $this->PMA_pow(1000, $d, 'pow'))+1); + if ($digits_left > $cur_digits) { + $d -= floor(($digits_left - $cur_digits)/3); } - $i = $pageNow; - $dist = 1; - while ($i >0) { - $dist = 2 * $dist; - $i = $pageNow - $dist; - if ($i > 0 && $i <= $x) { - $pages[] = $i; - } + if ($d < 0 && $only_down) { + $d = 0; } - // Since because of ellipsing of the current page some numbers may be - // double, we unify our array: - sort($pages); - $pages = array_unique($pages); - } + $value = round($value / ($this->PMA_pow(1000, $d, 'pow') / $dh)) /$dh; + $unit = $units[$d]; - foreach ($pages as $i) { - if ($i == $pageNow) { - $selected = 'selected="selected" style="font-weight: bold"'; + // If we dont want any zeros after the comma just add the thousand separator + if ($noTrailingZero) { + $value = $this->PMA_localizeNumber( + preg_replace('/(?<=\d)(?=(\d{3})+(?!\d))/', ',', $value) + ); } else { - $selected = ''; - } - $gotopage .= ' ' . "\n"; - } - - $gotopage .= ' '; - - return $gotopage; -} // end function - - -/** - * Prepare navigation for a list - * - * @param int $count number of elements in the list - * @param int $pos current position in the list - * @param array $_url_params url parameters - * @param string $script script name for form target - * @param string $frame target frame - * @param int $max_count maximum number of elements to display from the list - * - * @return string $list_navigator_html the html content - * - * @access public - * - * @todo use $pos from $_url_params - */ -function PMA_getListNavigator( - $count, $pos, $_url_params, $script, $frame, $max_count -) { - - $list_navigator_html = ''; - - if ($max_count < $count) { - - $list_navigator_html .= ($frame == 'frame_navigation') - ? '' . "\n"; + + if (! empty($tab['warning'])) { + $tab['class'] .= ' error'; + $tab['attr'] .= ' title="' . htmlspecialchars($tab['warning']) . '"'; } - } - - return $list_navigator_html; - -} + + // If there are any tab specific URL parameters, merge those with + // the general URL parameters + if (! empty($tab['url_params']) && is_array($tab['url_params'])) { + $url_params = array_merge($url_params, $tab['url_params']); + } + + // build the link + if (! empty($tab['link'])) { + $tab['link'] = htmlentities($tab['link']); + $tab['link'] = $tab['link'] . PMA_generate_common_url($url_params); + if (! empty($tab['args'])) { + foreach ($tab['args'] as $param => $value) { + $tab['link'] .= PMA_get_arg_separator('html') . urlencode($param) + . '=' . urlencode($value); + } + } + } + + if (! empty($tab['fragment'])) { + $tab['link'] .= $tab['fragment']; + } + + // display icon + if (isset($tab['icon']) + ) { + // avoid generating an alt tag, because it only illustrates + // the text that follows and if browser does not display + // images, the text is duplicated + $tab['text'] = $this->PMA_getImage(htmlentities($tab['icon'])) . $tab['text']; + + } elseif (empty($tab['text'])) { + // check to not display an empty link-text + $tab['text'] = '?'; + trigger_error( + 'empty linktext in function ' . __FUNCTION__ . '()', + E_USER_NOTICE + ); + } + + //Set the id for the tab, if set in the params + $id_string = ( empty($tab['id']) ? '' : ' id="'.$tab['id'].'" ' ); + $out = ''; + + if (! empty($tab['link'])) { + $out .= '' + . $tab['text'] . ''; + } else { + $out .= '' + . $tab['text'] . ''; + } + + $out .= ''; + return $out; + } // end of the 'PMA_generateHtmlTab()' function -/** - * replaces %u in given path with current user name - * - * example: - * - * $user_dir = PMA_userDir('/var/pma_tmp/%u/'); // '/var/pma_tmp/root/' - * - * - * - * @param string $dir with wildcard for user - * - * @return string per user directory - */ -function PMA_userDir($dir) -{ - // add trailing slash - if (substr($dir, -1) != '/') { - $dir .= '/'; + /** + * returns html-code for a tab navigation + * + * @param array $tabs one element per tab + * @param string $url_params additional URL parameters + * @param string $menu_id HTML id attribute for the menu container + * + * @return string html-code for tab-navigation + */ + public function PMA_generateHtmlTabs($tabs, $url_params, $menu_id = 'topmenu') + { + $tab_navigation = '' . "\n"; + + return $tab_navigation; } - return str_replace('%u', $GLOBALS['cfg']['Server']['user'], $dir); -} + /** + * Displays a link, or a button if the link's URL is too large, to + * accommodate some browsers' limitations + * + * @param string $url the URL + * @param string $message the link message + * @param mixed $tag_params string: js confirmation + * array: additional tag params (f.e. style="") + * @param boolean $new_form we set this to false when we are already in + * a form, to avoid generating nested forms + * @param boolean $strip_img whether to strip the image + * @param string $target target + * + * @return string the results to be echoed or saved in an array + */ + public function PMA_linkOrButton( + $url, $message, $tag_params = array(), + $new_form = true, $strip_img = false, $target = '' + ) { -/** - * returns html code for db link to default db page - * - * @param string $database database - * - * @return string html link to default db page - */ -function PMA_getDbLink($database = null) -{ - if (! strlen($database)) { - if (! strlen($GLOBALS['db'])) { + $url_length = strlen($url); + // with this we should be able to catch case of image upload + // into a (MEDIUM) BLOB; not worth generating even a form for these + if ($url_length > $GLOBALS['cfg']['LinkLengthLimit'] * 100) { return ''; } - $database = $GLOBALS['db']; - } else { - $database = PMA_unescapeMysqlWildcards($database); - } - - return '' . htmlspecialchars($database) . ''; -} -/** - * Prepare a lightbulb hint explaining a known external bug - * that affects a functionality - * - * @param string $functionality localized message explaining the func. - * @param string $component 'mysql' (eventually, 'php') - * @param string $minimum_version of this component - * @param string $bugref bug reference for this component - * - * @return void - */ -function PMA_getExternalBug($functionality, $component, $minimum_version, $bugref) -{ - $ext_but_html = ''; - if (($component == 'mysql') && (PMA_MYSQL_INT_VERSION < $minimum_version)) { - $ext_but_html .= PMA_showHint( - sprintf( - __('The %s functionality is affected by a known bug, see %s'), - $functionality, - PMA_linkURL('http://bugs.mysql.com/') . $bugref - ) - ); - } - return $ext_but_html; -} - - -/** - * Returns a HTML checkbox - * - * @param string $html_field_name the checkbox HTML field - * @param string $label label for checkbox - * @param boolean $checked is it initially checked? - * @param boolean $onclick should it submit the form on click? - * - * @return string HTML for the checkbox - */ -function PMA_getCheckbox($html_field_name, $label, $checked, $onclick) -{ - return ''; -} - - -/** - * Generates a set of radio HTML fields - * - * @param string $html_field_name the radio HTML field - * @param array $choices the choices values and labels - * @param string $checked_choice the choice to check by default - * @param boolean $line_break whether to add an HTML line break after a choice - * @param boolean $escape_label whether to use htmlspecialchars() on label - * @param string $class enclose each choice with a div of this class - * - * @return string set of html radio fiels - */ -function PMA_getRadioFields($html_field_name, $choices, $checked_choice = '', - $line_break = true, $escape_label = true, $class='' -) { - - $radio_html = ''; - - foreach ($choices as $choice_value => $choice_label) { - - if (! empty($class)) { - $radio_html .= '
'; + if (! is_array($tag_params)) { + $tmp = $tag_params; + $tag_params = array(); + if (! empty($tmp)) { + $tag_params['onclick'] = 'return confirmLink(this, \'' + . PMA_escapeJsString($tmp) . '\')'; + } + unset($tmp); } - - $html_field_id = $html_field_name . '_' . $choice_value; - $radio_html .= '' - . ($escape_label - ? htmlspecialchars($choice_label) - : $choice_label) - . ''; - - if ($line_break) { - $radio_html .= '
'; + + $tag_params_strings = array(); + foreach ($tag_params as $par_name => $par_value) { + // htmlspecialchars() only on non javascript + $par_value = substr($par_name, 0, 2) == 'on' + ? $par_value + : htmlspecialchars($par_value); + $tag_params_strings[] = $par_name . '="' . $par_value . '"'; } - - if (! empty($class)) { - $radio_html .= '
'; + + $displayed_message = ''; + // Add text if not already added + if (stristr($message, ''; } - $radio_html .= "\n"; - } - return $radio_html; - -} - - -/** - * Generates and returns an HTML dropdown - * - * @param string $select_name name for the select element - * @param array $choices choices values - * @param string $active_choice the choice to select by default - * @param string $id id of the select element; can be different in - * case the dropdown is present more than once - * on the page - * - * @return string html content - * - * @todo support titles - */ -function PMA_getDropdown($select_name, $choices, $active_choice, $id) -{ - $result = ''; - - return $result; -} - - -/** - * Generates a slider effect (jQjuery) - * Takes care of generating the initial
and the link - * controlling the slider; you have to generate the
yourself - * after the sliding section. - * - * @param string $id the id of the
on which to apply the effect - * @param string $message the message to show as a link - * - * @return string html div element - * - */ -function PMA_getDivForSliderEffect($id, $message) -{ - if ($GLOBALS['cfg']['InitialSlidersState'] == 'disabled') { - return '
'; - } - /** - * Bad hack on the next line. document.write() conflicts with jQuery, - * hence, opening the
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 '