Initial commit

This commit is contained in:
2022-11-21 09:47:28 +01:00
commit 76cec83d26
11652 changed files with 1980467 additions and 0 deletions

557
#pma/libraries/Advisor.php Normal file
View File

@ -0,0 +1,557 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* A simple rules engine, that parses and executes the rules in advisory_rules.txt.
* Adjusted to phpMyAdmin.
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use \Exception;
require_once 'libraries/advisor.lib.php';
/**
* Advisor class
*
* @package PhpMyAdmin
*/
class Advisor
{
protected $variables;
protected $parseResult;
protected $runResult;
/**
* Get variables
*
* @return mixed
*/
public function getVariables()
{
return $this->variables;
}
/**
* Set variables
*
* @param array $variables Variables
*
* @return Advisor
*/
public function setVariables($variables)
{
$this->variables = $variables;
return $this;
}
/**
* Set a variable and its value
*
* @param string|int $variable Variable to set
* @param mixed $value Value to set
*
* @return $this
*/
public function setVariable($variable, $value)
{
$this->variables[$variable] = $value;
return $this;
}
/**
* Get parseResult
*
* @return mixed
*/
public function getParseResult()
{
return $this->parseResult;
}
/**
* Set parseResult
*
* @param array $parseResult Parse result
*
* @return Advisor
*/
public function setParseResult($parseResult)
{
$this->parseResult = $parseResult;
return $this;
}
/**
* Get runResult
*
* @return mixed
*/
public function getRunResult()
{
return $this->runResult;
}
/**
* Set runResult
*
* @param array $runResult Run result
*
* @return Advisor
*/
public function setRunResult($runResult)
{
$this->runResult = $runResult;
return $this;
}
/**
* Parses and executes advisor rules
*
* @return array with run and parse results
*/
public function run()
{
// HowTo: A simple Advisory system in 3 easy steps.
// Step 1: Get some variables to evaluate on
$this->setVariables(
array_merge(
$GLOBALS['dbi']->fetchResult('SHOW GLOBAL STATUS', 0, 1),
$GLOBALS['dbi']->fetchResult('SHOW GLOBAL VARIABLES', 0, 1)
)
);
// Add total memory to variables as well
include_once 'libraries/sysinfo.lib.php';
$sysinfo = PMA_getSysInfo();
$memory = $sysinfo->memory();
$this->variables['system_memory']
= isset($memory['MemTotal']) ? $memory['MemTotal'] : 0;
// Step 2: Read and parse the list of rules
$this->setParseResult(static::parseRulesFile());
// Step 3: Feed the variables to the rules and let them fire. Sets
// $runResult
$this->runRules();
return array(
'parse' => array('errors' => $this->parseResult['errors']),
'run' => $this->runResult
);
}
/**
* Stores current error in run results.
*
* @param string $description description of an error.
* @param Exception $exception exception raised
*
* @return void
*/
public function storeError($description, $exception)
{
$this->runResult['errors'][] = $description
. ' '
. sprintf(
__('PHP threw following error: %s'),
$exception->getMessage()
);
}
/**
* Executes advisor rules
*
* @return boolean
*/
public function runRules()
{
$this->setRunResult(
array(
'fired' => array(),
'notfired' => array(),
'unchecked' => array(),
'errors' => array(),
)
);
foreach ($this->parseResult['rules'] as $rule) {
$this->variables['value'] = 0;
$precond = true;
if (isset($rule['precondition'])) {
try {
$precond = $this->ruleExprEvaluate($rule['precondition']);
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed evaluating precondition for rule \'%s\'.'),
$rule['name']
),
$e
);
continue;
}
}
if (! $precond) {
$this->addRule('unchecked', $rule);
} else {
try {
$value = $this->ruleExprEvaluate($rule['formula']);
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed calculating value for rule \'%s\'.'),
$rule['name']
),
$e
);
continue;
}
$this->variables['value'] = $value;
try {
if ($this->ruleExprEvaluate($rule['test'])) {
$this->addRule('fired', $rule);
} else {
$this->addRule('notfired', $rule);
}
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed running test for rule \'%s\'.'),
$rule['name']
),
$e
);
}
}
}
return true;
}
/**
* Escapes percent string to be used in format string.
*
* @param string $str string to escape
*
* @return string
*/
public static function escapePercent($str)
{
return preg_replace('/%( |,|\.|$|\(|\)|<|>)/', '%%\1', $str);
}
/**
* Wrapper function for translating.
*
* @param string $str the string
* @param string $param the parameters
*
* @return string
*/
public function translate($str, $param = null)
{
$string = _gettext(self::escapePercent($str));
if (! is_null($param)) {
$params = $this->ruleExprEvaluate('array(' . $param . ')');
} else {
$params = array();
}
return vsprintf($string, $params);
}
/**
* Splits justification to text and formula.
*
* @param array $rule the rule
*
* @return string[]
*/
public static function splitJustification($rule)
{
$jst = preg_split('/\s*\|\s*/', $rule['justification'], 2);
if (count($jst) > 1) {
return array($jst[0], $jst[1]);
}
return array($rule['justification']);
}
/**
* Adds a rule to the result list
*
* @param string $type type of rule
* @param array $rule rule itself
*
* @return void
*/
public function addRule($type, $rule)
{
switch ($type) {
case 'notfired':
case 'fired':
$jst = self::splitJustification($rule);
if (count($jst) > 1) {
try {
/* Translate */
$str = $this->translate($jst[0], $jst[1]);
} catch (Exception $e) {
$this->storeError(
sprintf(
__('Failed formatting string for rule \'%s\'.'),
$rule['name']
),
$e
);
return;
}
$rule['justification'] = $str;
} else {
$rule['justification'] = $this->translate($rule['justification']);
}
$rule['id'] = $rule['name'];
$rule['name'] = $this->translate($rule['name']);
$rule['issue'] = $this->translate($rule['issue']);
// Replaces {server_variable} with 'server_variable'
// linking to server_variables.php
$rule['recommendation'] = preg_replace(
'/\{([a-z_0-9]+)\}/Ui',
'<a href="server_variables.php' . PMA_URL_getCommon()
. '&filter=\1">\1</a>',
$this->translate($rule['recommendation'])
);
// Replaces external Links with PMA_linkURL() generated links
$rule['recommendation'] = preg_replace_callback(
'#href=("|\')(https?://[^\1]+)\1#i',
array($this, 'replaceLinkURL'),
$rule['recommendation']
);
break;
}
$this->runResult[$type][] = $rule;
}
/**
* Callback for wrapping links with PMA_linkURL
*
* @param array $matches List of matched elements form preg_replace_callback
*
* @return string Replacement value
*/
private function replaceLinkURL($matches)
{
return 'href="' . PMA_linkURL($matches[2]) . '" target="_blank" rel="noopener noreferrer"';
}
/**
* Callback for evaluating fired() condition.
*
* @param array $matches List of matched elements form preg_replace_callback
*
* @return string Replacement value
*/
private function ruleExprEvaluateFired($matches)
{
// No list of fired rules
if (!isset($this->runResult['fired'])) {
return '0';
}
// Did matching rule fire?
foreach ($this->runResult['fired'] as $rule) {
if ($rule['id'] == $matches[2]) {
return '1';
}
}
return '0';
}
/**
* Callback for evaluating variables in expression.
*
* @param array $matches List of matched elements form preg_replace_callback
*
* @return string Replacement value
*/
private function ruleExprEvaluateVariable($matches)
{
if (! isset($this->variables[$matches[1]])) {
return $matches[1];
}
if (is_numeric($this->variables[$matches[1]])) {
return $this->variables[$matches[1]];
} else {
return '\'' . addslashes($this->variables[$matches[1]]) . '\'';
}
}
/**
* Runs a code expression, replacing variable names with their respective
* values
*
* @param string $expr expression to evaluate
*
* @return integer result of evaluated expression
*
* @throws Exception
*/
public function ruleExprEvaluate($expr)
{
// Evaluate fired() conditions
$expr = preg_replace_callback(
'/fired\s*\(\s*(\'|")(.*)\1\s*\)/Ui',
array($this, 'ruleExprEvaluateFired'),
$expr
);
// Evaluate variables
$expr = preg_replace_callback(
'/\b(\w+)\b/',
array($this, 'ruleExprEvaluateVariable'),
$expr
);
$value = 0;
$err = 0;
// Actually evaluate the code
ob_start();
try {
eval('$value = ' . $expr . ';');
$err = ob_get_contents();
} catch (Exception $e) {
// In normal operation, there is just output in the buffer,
// but when running under phpunit, error in eval raises exception
$err = $e->getMessage();
}
ob_end_clean();
// Error handling
if ($err) {
throw new Exception(
strip_tags($err)
. '<br />Executed code: $value = ' . htmlspecialchars($expr) . ';'
);
}
return $value;
}
/**
* Reads the rule file into an array, throwing errors messages on syntax
* errors.
*
* @return array with parsed data
*/
public static function parseRulesFile()
{
$filename = 'libraries/advisory_rules.txt';
$file = file($filename, FILE_IGNORE_NEW_LINES);
$errors = array();
$rules = array();
$lines = array();
if ($file === FALSE) {
$errors[] = sprintf(
__('Error in reading file: The file \'%s\' does not exist or is not readable!'),
$filename
);
return array('rules' => $rules, 'lines' => $lines, 'errors' => $errors);
}
$ruleSyntax = array(
'name', 'formula', 'test', 'issue', 'recommendation', 'justification'
);
$numRules = count($ruleSyntax);
$numLines = count($file);
$ruleNo = -1;
$ruleLine = -1;
for ($i = 0; $i < $numLines; $i++) {
$line = $file[$i];
if ($line == "" || $line[0] == '#') {
continue;
}
// Reading new rule
if (substr($line, 0, 4) == 'rule') {
if ($ruleLine > 0) {
$errors[] = sprintf(
__(
'Invalid rule declaration on line %1$s, expected line '
. '%2$s of previous rule.'
),
$i + 1,
$ruleSyntax[$ruleLine++]
);
continue;
}
if (preg_match("/rule\s'(.*)'( \[(.*)\])?$/", $line, $match)) {
$ruleLine = 1;
$ruleNo++;
$rules[$ruleNo] = array('name' => $match[1]);
$lines[$ruleNo] = array('name' => $i + 1);
if (isset($match[3])) {
$rules[$ruleNo]['precondition'] = $match[3];
$lines[$ruleNo]['precondition'] = $i + 1;
}
} else {
$errors[] = sprintf(
__('Invalid rule declaration on line %s.'),
$i + 1
);
}
continue;
} else {
if ($ruleLine == -1) {
$errors[] = sprintf(
__('Unexpected characters on line %s.'),
$i + 1
);
}
}
// Reading rule lines
if ($ruleLine > 0) {
if (!isset($line[0])) {
continue; // Empty lines are ok
}
// Non tabbed lines are not
if ($line[0] != "\t") {
$errors[] = sprintf(
__(
'Unexpected character on line %1$s. Expected tab, but '
. 'found "%2$s".'
),
$i + 1,
$line[0]
);
continue;
}
$rules[$ruleNo][$ruleSyntax[$ruleLine]] = chop(
mb_substr($line, 1)
);
$lines[$ruleNo][$ruleSyntax[$ruleLine]] = $i + 1;
++$ruleLine;
}
// Rule complete
if ($ruleLine == $numRules) {
$ruleLine = -1;
}
}
return array('rules' => $rules, 'lines' => $lines, 'errors' => $errors);
}
}

1699
#pma/libraries/Config.php Normal file

File diff suppressed because it is too large Load Diff

389
#pma/libraries/Console.php Normal file
View File

@ -0,0 +1,389 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Used to render the console of PMA's pages
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
if (! defined('PHPMYADMIN')) {
exit;
}
require_once 'libraries/bookmark.lib.php';
/**
* Class used to output the console
*
* @package PhpMyAdmin
*/
class Console
{
/**
* Whether to display anything
*
* @access private
* @var bool
*/
private $_isEnabled;
/**
* Creates a new class instance
*/
public function __construct()
{
$this->_isEnabled = true;
}
/**
* Whether we are servicing an ajax request.
* We can't simply use $GLOBALS['is_ajax_request']
* here since it may have not been initialised yet.
*
* @access private
* @var bool
*/
private $_isAjax;
/**
* Set the ajax flag to indicate whether
* we are servicing an ajax request
*
* @param bool $isAjax Whether we are servicing an ajax request
*
* @return void
*/
public function setAjax($isAjax)
{
$this->_isAjax = (boolean) $isAjax;
}
/**
* Disables the rendering of the footer
*
* @return void
*/
public function disable()
{
$this->_isEnabled = false;
}
/**
* Renders the bookmark content
*
* @access public
* @return string
*/
public static function getBookmarkContent()
{
$output = '';
$cfgBookmark = PMA_Bookmark_getParams();
if ($cfgBookmark) {
$tpl_bookmark_actions
= '<span class="action collapse">' . __('Collapse') . '</span> '
. '<span class="action expand">' . __('Expand') . '</span> '
. '<span class="action requery">' . __('Requery') . '</span> '
. '<span class="action edit_bookmark">' . __('Edit') . '</span> '
. '<span class="action delete_bookmark">' . __('Delete')
. '</span> '
. '<span class="text targetdb">' . __('Database')
. ': <span>%s</span></span>';
$bookmarks = PMA_Bookmark_getList();
$output .= '<div class="message welcome"><span>';
$count_bookmarks = count($bookmarks);
if ($count_bookmarks > 0) {
$output .= sprintf(
_ngettext(
'Showing %1$d bookmark (both private and shared)',
'Showing %1$d bookmarks (both private and shared)',
$count_bookmarks
),
$count_bookmarks
);
} else {
$output .= __('No bookmarks');
}
unset($count_bookmarks, $private_message, $shared_message);
$output .= '</span></div>';
foreach ($bookmarks as $val) {
$output .= '<div class="message collapsed bookmark" bookmarkid="'
. $val['id'] . '" targetdb="' . htmlspecialchars($val['db'])
. '"><div class="action_content">'
. sprintf($tpl_bookmark_actions, htmlspecialchars($val['db']))
. '</div><span class="bookmark_label '
. ($val['shared'] ? 'shared' : '') . '">'
. htmlspecialchars($val['label'])
. '</span> <span class="query">'
. htmlspecialchars($val['query'])
. '</span></div>';
}
}
return $output;
}
/**
* Returns the list of JS scripts required by console
*
* @return array list of scripts
*/
public function getScripts()
{
return array('console.js');
}
/**
* Gets the history
*
* @param string $tpl_query_actions the template for query actions
*
* @return string $output the generated HTML for history
*
* @access private
*
*/
private function _getHistory($tpl_query_actions)
{
$output = '';
$_sql_history = PMA_getHistory($GLOBALS['cfg']['Server']['user']);
if (! empty($_sql_history)) {
foreach (array_reverse($_sql_history) as $record) {
$isSelect = preg_match(
'@^SELECT[[:space:]]+@i', $record['sqlquery']
);
$output .= '<div class="message history collapsed hide'
. ($isSelect ? ' select' : '')
. '" targetdb="'
. htmlspecialchars($record['db'])
. '" targettable="' . htmlspecialchars($record['table'])
. '"><div class="action_content">'
. sprintf(
$tpl_query_actions,
htmlspecialchars($record['db']),
(isset($record['timevalue'])
? $record['timevalue']
: __('During current session')
)
)
. '</div><span class="query">'
. htmlspecialchars($record['sqlquery'])
. '</span></div>';
}
}
return $output;
}
/**
* Renders the console
*
* @access public
* @return string
*/
public function getDisplay()
{
$output = '';
if ((! $this->_isAjax) && $this->_isEnabled) {
$cfgBookmark = PMA_Bookmark_getParams();
$output .= '<div id="pma_console_container"><div id="pma_console">';
// The templates, use sprintf() to output them
// There're white space at the end of every <span>,
// for double-click selection
$tpl_query_actions = '<span class="action collapse">' . __('Collapse')
. '</span> '
. '<span class="action expand">' . __('Expand') . '</span> '
. '<span class="action requery">' . __('Requery') . '</span> '
. '<span class="action edit">' . __('Edit') . '</span> '
. '<span class="action explain">' . __('Explain') . '</span> '
. '<span class="action profiling">' . __('Profiling') . '</span> '
. ($cfgBookmark ? '<span class="action bookmark">'
. __('Bookmark') . '</span> ' : '')
. '<span class="text failed">' . __('Query failed') . '</span> '
. '<span class="text targetdb">' . __('Database')
. ': <span>%s</span></span> '
. '<span class="text query_time">' . __(
'Queried time'
) . ': <span>%s</span></span> ';
// Console toolbar
$output .= '<div class="toolbar collapsed">';
$output .= '<div class="switch_button console_switch">';
$output .= Util::getImage('console.png', __('SQL Query Console'));
$output .= '<span>' . __('Console') . '</span></div>';
$output .= '<div class="button clear"><span>'
. __('Clear') . '</span></div>';
$output .= '<div class="button history"><span>'
. __('History') . '</span></div>';
$output .= '<div class="button options"><span>'
. __('Options') . '</span></div>';
if ($cfgBookmark) {
$output .= '<div class="button bookmarks"><span>'
. __('Bookmarks') . '</span></div>';
}
$output .= '<div class="button debug hide"><span>'
. __('Debug SQL') . '</span></div>';
$output .= '</div>'; // Toolbar end
// Console messages
$output .= '<div class="content">';
$output .= '<div class="console_message_container">'
. '<div class="message welcome"><span>'
. '<span id="instructions-0">'
. __('Press Ctrl+Enter to execute query') . '</span>'
. '<span class="hide" id="instructions-1">'
. __('Press Enter to execute query') . '</span>'
. '</span></div>';
$output .= $this->_getHistory($tpl_query_actions);
$output .= '</div>'; // .console_message_container
$output .= '<div class="query_input">'
. '<span class="console_query_input"></span>'
. '</div>';
$output .= '</div>'; // Messages end
// Dark the console while other cards cover it
$output .= '<div class="mid_layer"></div>';
// Debug SQL card
$output .= '<div class="card" id="debug_console">';
$output .= '<div class="toolbar">'
. '<div class="button order order_asc">'
. '<span>' . __('ascending') . '</span>'
. '</div>'
. '<div class="button order order_desc">'
. '<span>' . __('descending') . '</span>'
. '</div>'
. '<div class="text">'
. '<span>' . __('Order:') . '</span>'
. '</div>'
. '<div class="switch_button">'
. '<span>' . __('Debug SQL') . '</span>'
. '</div>'
. '<div class="button order_by sort_count">'
. '<span>' . __('Count') . '</span>'
. '</div>'
. '<div class="button order_by sort_exec">'
. '<span>' . __('Execution order') . '</span>'
. '</div>'
. '<div class="button order_by sort_time">'
. '<span>' . __('Time taken') . '</span>'
. '</div>'
. '<div class="text">'
. '<span>' . __('Order by:') . '</span>'
. '</div>'
. '<div class="button group_queries">'
. '<span>' . __('Group queries') . '</span>'
. '</div>'
. '<div class="button ungroup_queries">'
. '<span>' . __('Ungroup queries') . '</span>'
. '</div>'
. '</div>'; // Toolbar
$output .= '<div class="content debug">';
$output .= '<div class="message welcome"></div>';
$output .= '<div class="debugLog"></div>';
$output .= '</div>'; // Content
$output .= '<div class="templates">'
. '<div class="debug_query action_content">'
. '<span class="action collapse">' . __('Collapse') . '</span> '
. '<span class="action expand">' . __('Expand') . '</span> '
. '<span class="action dbg_show_trace">' . __('Show trace')
. '</span> '
. '<span class="action dbg_hide_trace">' . __('Hide trace')
. '</span> '
. '<span class="text count hide">' . __('Count:')
. ' <span></span></span>'
. '<span class="text time">' . __('Time taken:')
. ' <span></span></span>'
. '</div>'
. '</div>'; // Template
$output .= '</div>'; // Debug SQL card
// Bookmarks card:
if ($cfgBookmark) {
$output .= '<div class="card" id="pma_bookmarks">';
$output .= '<div class="toolbar">'
. '<div class="switch_button"><span>' . __('Bookmarks')
. '</span></div>';
$output .= '<div class="button refresh"><span>'
. __('Refresh') . '</span></div>';
$output .= '<div class="button add"><span>'
. __('Add') . '</span></div>';
$output .= '</div><div class="content bookmark">';
$output .= static::getBookmarkContent();
$output .= '</div>';
$output .= '<div class="mid_layer"></div>';
$output .= '<div class="card add">';
$output .= '<div class="toolbar">'
. '<div class="switch_button"><span>'
. __('Add bookmark')
. '</span></div>';
$output .= '</div><div class="content add_bookmark">'
. '<div class="options">'
. '<label>' . __('Label')
. ': <input type="text" name="label"></label> '
. '<label>' . __('Target database')
. ': <input type="text" name="targetdb"></label> '
. '<label><input type="checkbox" name="shared">'
. __('Share this bookmark') . '</label>'
. '<button type="submit" name="submit">Ok</button>'
. '</div>' // .options
. '<div class="query_input">'
. '<span class="bookmark_add_input"></span></div>';
$output .= '</div>';
$output .= '</div>'; // Add bookmark card
$output .= '</div>'; // Bookmarks card
}
// Options card:
$output .= '<div class="card" id="pma_console_options">';
$output .= '<div class="toolbar">'
. '<div class="switch_button"><span>' . __('Options')
. '</span></div>';
$output .= '<div class="button default"><span>'
. __('Set default') . '</span></div>';
$output .= '</div><div class="content">'
. '<label><input type="checkbox" name="always_expand">'
. __('Always expand query messages') . '</label><br>'
. '<label><input type="checkbox" name="start_history">'
. __('Show query history at start') . '</label><br>'
. '<label><input type="checkbox" name="current_query">'
. __('Show current browsing query') . '</label><br>'
. '<label><input type="checkbox" name="enter_executes">'
. __(
'Execute queries on Enter and insert new line with Shift + '
. 'Enter. To make this permanent, view settings.'
) . '</label><br>'
. '<label><input type="checkbox" name="dark_theme">'
. __('Switch to dark theme') . '</label><br>'
. '</div>';
$output .= '</div>'; // Options card
$output .= '<div class="templates">'
// Templates for console message actions
. '<div class="query_actions">'
. sprintf($tpl_query_actions, '', '')
. '</div>'
. '</div>';
$output .= '</div></div>'; // #console and #pma_console_container ends
}
return $output;
}
}

File diff suppressed because it is too large Load Diff

100
#pma/libraries/DbList.php Normal file
View File

@ -0,0 +1,100 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* holds the DbList class
*
* @package PhpMyAdmin
*
*/
namespace PMA\libraries;
/**
* holds the DbList class
*
* @package PhpMyAdmin
*
* @property object $userlink
* @property object $controllink
*/
class DbList
{
/**
* Holds database list
*
* @var ListDatabase
*/
protected $databases = null;
/**
* DBMS user link
*
* @var object
*/
protected $userlink = null;
/**
* DBMS control link
*
* @var object
*/
protected $controllink = null;
/**
* magic access to protected/inaccessible members/properties
*
* @param string $param parameter name
*
* @return mixed
* @see https://php.net/language.oop5.overloading
*/
public function __get($param)
{
switch ($param) {
case 'databases' :
return $this->getDatabaseList();
case 'userlink' :
return $this->userlink;
case 'controllink' :
return $this->controllink;
}
return null;
}
/**
* magic access to protected/inaccessible members/properties
*
* @param string $param parameter name
* @param mixed $value value to set
*
* @return void
* @see https://php.net/language.oop5.overloading
*/
public function __set($param, $value)
{
switch ($param) {
case 'userlink' :
$this->userlink = $value;
break;
case 'controllink' :
$this->controllink = $value;
break;
}
}
/**
* Accessor to PMA::$databases
*
* @return ListDatabase
*/
public function getDatabaseList()
{
if (null === $this->databases) {
$this->databases = new ListDatabase(
$this->userlink
);
}
return $this->databases;
}
}

1983
#pma/libraries/DbQbe.php Normal file

File diff suppressed because it is too large Load Diff

490
#pma/libraries/DbSearch.php Normal file
View File

@ -0,0 +1,490 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles Database Search
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Class to handle database search
*
* @package PhpMyAdmin
*/
class DbSearch
{
/**
* Database name
*
* @access private
* @var string
*/
private $_db;
/**
* Table Names
*
* @access private
* @var array
*/
private $_tables_names_only;
/**
* Type of search
*
* @access private
* @var array
*/
private $_searchTypes;
/**
* Already set search type
*
* @access private
* @var integer
*/
private $_criteriaSearchType;
/**
* Already set search type's description
*
* @access private
* @var string
*/
private $_searchTypeDescription;
/**
* Search string/regexp
*
* @access private
* @var string
*/
private $_criteriaSearchString;
/**
* Criteria Tables to search in
*
* @access private
* @var array
*/
private $_criteriaTables;
/**
* Restrict the search to this column
*
* @access private
* @var string
*/
private $_criteriaColumnName;
/**
* Public Constructor
*
* @param string $db Database name
*/
public function __construct($db)
{
$this->_db = $db;
// Sets criteria parameters
$this->_setSearchParams();
}
/**
* Sets search parameters
*
* @return void
*/
private function _setSearchParams()
{
$this->_tables_names_only = $GLOBALS['dbi']->getTables($this->_db);
$this->_searchTypes = array(
'1' => __('at least one of the words'),
'2' => __('all words'),
'3' => __('the exact phrase'),
'4' => __('as regular expression'),
);
if (empty($_REQUEST['criteriaSearchType'])
|| ! is_string($_REQUEST['criteriaSearchType'])
|| ! array_key_exists(
$_REQUEST['criteriaSearchType'],
$this->_searchTypes
)
) {
$this->_criteriaSearchType = 1;
unset($_REQUEST['submit_search']);
} else {
$this->_criteriaSearchType = (int) $_REQUEST['criteriaSearchType'];
$this->_searchTypeDescription
= $this->_searchTypes[$_REQUEST['criteriaSearchType']];
}
if (empty($_REQUEST['criteriaSearchString'])
|| ! is_string($_REQUEST['criteriaSearchString'])
) {
$this->_criteriaSearchString = '';
unset($_REQUEST['submit_search']);
} else {
$this->_criteriaSearchString = $_REQUEST['criteriaSearchString'];
}
$this->_criteriaTables = array();
if (empty($_REQUEST['criteriaTables'])
|| ! is_array($_REQUEST['criteriaTables'])
) {
unset($_REQUEST['submit_search']);
} else {
$this->_criteriaTables = array_intersect(
$_REQUEST['criteriaTables'], $this->_tables_names_only
);
}
if (empty($_REQUEST['criteriaColumnName'])
|| ! is_string($_REQUEST['criteriaColumnName'])
) {
unset($this->_criteriaColumnName);
} else {
$this->_criteriaColumnName = $GLOBALS['dbi']->escapeString(
$_REQUEST['criteriaColumnName'], true
);
}
}
/**
* Builds the SQL search query
*
* @param string $table The table name
*
* @return array 3 SQL queries (for count, display and delete results)
*
* @todo can we make use of fulltextsearch IN BOOLEAN MODE for this?
* PMA_backquote
* DatabaseInterface::freeResult
* DatabaseInterface::fetchAssoc
* $GLOBALS['db']
* explode
* count
* strlen
*/
private function _getSearchSqls($table)
{
// Statement types
$sqlstr_select = 'SELECT';
$sqlstr_delete = 'DELETE';
// Table to use
$sqlstr_from = ' FROM '
. Util::backquote($GLOBALS['db']) . '.'
. Util::backquote($table);
// Gets where clause for the query
$where_clause = $this->_getWhereClause($table);
// Builds complete queries
$sql = array();
$sql['select_columns'] = $sqlstr_select . ' * ' . $sqlstr_from
. $where_clause;
// here, I think we need to still use the COUNT clause, even for
// VIEWs, anyway we have a WHERE clause that should limit results
$sql['select_count'] = $sqlstr_select . ' COUNT(*) AS `count`'
. $sqlstr_from . $where_clause;
$sql['delete'] = $sqlstr_delete . $sqlstr_from . $where_clause;
return $sql;
}
/**
* Provides where clause for building SQL query
*
* @param string $table The table name
*
* @return string The generated where clause
*/
private function _getWhereClause($table)
{
// Columns to select
$allColumns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table);
$likeClauses = array();
// Based on search type, decide like/regex & '%'/''
$like_or_regex = (($this->_criteriaSearchType == 4) ? 'REGEXP' : 'LIKE');
$automatic_wildcard = (($this->_criteriaSearchType < 3) ? '%' : '');
// For "as regular expression" (search option 4), LIKE won't be used
// Usage example: If user is searching for a literal $ in a regexp search,
// he should enter \$ as the value.
$criteriaSearchStringEscaped = $GLOBALS['dbi']->escapeString(
$this->_criteriaSearchString
);
// Extract search words or pattern
$search_words = (($this->_criteriaSearchType > 2)
? array($criteriaSearchStringEscaped)
: explode(' ', $criteriaSearchStringEscaped));
foreach ($search_words as $search_word) {
// Eliminates empty values
if (mb_strlen($search_word) === 0) {
continue;
}
$likeClausesPerColumn = array();
// for each column in the table
foreach ($allColumns as $column) {
if (! isset($this->_criteriaColumnName)
|| mb_strlen($this->_criteriaColumnName) == 0
|| $column['Field'] == $this->_criteriaColumnName
) {
$column = 'CONVERT(' . Util::backquote($column['Field'])
. ' USING utf8)';
$likeClausesPerColumn[] = $column . ' ' . $like_or_regex . ' '
. "'"
. $automatic_wildcard . $search_word . $automatic_wildcard
. "'";
}
} // end for
if (count($likeClausesPerColumn) > 0) {
$likeClauses[] = implode(' OR ', $likeClausesPerColumn);
}
} // end for
// Use 'OR' if 'at least one word' is to be searched, else use 'AND'
$implode_str = ($this->_criteriaSearchType == 1 ? ' OR ' : ' AND ');
if (empty($likeClauses)) {
// this could happen when the "inside column" does not exist
// in any selected tables
$where_clause = ' WHERE FALSE';
} else {
$where_clause = ' WHERE ('
. implode(') ' . $implode_str . ' (', $likeClauses)
. ')';
}
return $where_clause;
}
/**
* Displays database search results
*
* @return string HTML for search results
*/
public function getSearchResults()
{
$html_output = '';
// Displays search string
$html_output .= '<br />'
. '<table class="data">'
. '<caption class="tblHeaders">'
. sprintf(
__('Search results for "<i>%s</i>" %s:'),
htmlspecialchars($this->_criteriaSearchString),
$this->_searchTypeDescription
)
. '</caption>';
$num_search_result_total = 0;
$odd_row = true;
// For each table selected as search criteria
foreach ($this->_criteriaTables as $each_table) {
// Gets the SQL statements
$newsearchsqls = $this->_getSearchSqls($each_table);
// Executes the "COUNT" statement
$res_cnt = $GLOBALS['dbi']->fetchValue($newsearchsqls['select_count']);
$num_search_result_total += $res_cnt;
// Gets the result row's HTML for a table
$html_output .= $this->_getResultsRow(
$each_table, $newsearchsqls, $odd_row, $res_cnt
);
$odd_row = ! $odd_row;
} // end for
$html_output .= '</table>';
// Displays total number of matches
if (count($this->_criteriaTables) > 1) {
$html_output .= '<p>';
$html_output .= sprintf(
_ngettext(
'<b>Total:</b> <i>%s</i> match',
'<b>Total:</b> <i>%s</i> matches',
$num_search_result_total
),
$num_search_result_total
);
$html_output .= '</p>';
}
return $html_output;
}
/**
* Provides search results row with browse/delete links.
* (for a table)
*
* @param string $each_table One of the tables on which search was performed
* @param array $newsearchsqls Contains SQL queries
* @param bool $odd_row For displaying contrasting table rows
* @param integer $res_cnt Number of results found
*
* @return string HTML row
*/
private function _getResultsRow($each_table, $newsearchsqls, $odd_row, $res_cnt)
{
$this_url_params = array(
'db' => $GLOBALS['db'],
'table' => $each_table,
'goto' => 'db_sql.php',
'pos' => 0,
'is_js_confirmed' => 0,
);
// Start forming search results row
$html_output = '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
// Displays results count for a table
$html_output .= '<td>';
$html_output .= sprintf(
_ngettext(
'%1$s match in <strong>%2$s</strong>',
'%1$s matches in <strong>%2$s</strong>', $res_cnt
),
$res_cnt, htmlspecialchars($each_table)
);
$html_output .= '</td>';
// Displays browse/delete link if result count > 0
if ($res_cnt > 0) {
$this_url_params['db'] = htmlspecialchars($GLOBALS['db']);
$this_url_params['table'] = htmlspecialchars($each_table);
$browse_result_path = 'sql.php' . PMA_URL_getCommon($this_url_params);
$html_output .= '<td><a name="browse_search" '
. ' class="ajax browse_results" href="'
. $browse_result_path . '" '
. 'data-browse-sql="'
. htmlspecialchars($newsearchsqls['select_columns']). '" '
. 'data-table-name="' . htmlspecialchars($each_table) . '" >'
. __('Browse') . '</a></td>';
$delete_result_path = $browse_result_path;
$html_output .= '<td><a name="delete_search" class="ajax delete_results"'
. ' href="' . $delete_result_path . '"'
. ' data-delete-sql="' . htmlspecialchars($newsearchsqls['delete']) . '"'
. ' data-table-name="' . htmlspecialchars($each_table) . '" >'
. __('Delete') . '</a></td>';
} else {
$html_output .= '<td>&nbsp;</td>'
. '<td>&nbsp;</td>';
}// end if else
$html_output .= '</tr>';
return $html_output;
}
/**
* Provides the main search form's html
*
* @return string HTML for selection form
*/
public function getSelectionForm()
{
$html_output = '<a id="db_search"></a>';
$html_output .= '<form id="db_search_form"'
. ' class="ajax lock-page"'
. ' method="post" action="db_search.php" name="db_search">';
$html_output .= PMA_URL_getHiddenInputs($GLOBALS['db']);
$html_output .= '<fieldset>';
// set legend caption
$html_output .= '<legend>' . __('Search in database') . '</legend>';
$html_output .= '<table class="formlayout">';
// inputbox for search phrase
$html_output .= '<tr>';
$html_output .= '<td>' . __('Words or values to search for (wildcard: "%"):')
. '</td>';
$html_output .= '<td><input type="text"'
. ' name="criteriaSearchString" size="60"'
. ' value="' . htmlspecialchars($this->_criteriaSearchString) . '" />';
$html_output .= '</td>';
$html_output .= '</tr>';
// choices for types of search
$html_output .= '<tr>';
$html_output .= '<td class="right vtop">' . __('Find:') . '</td>';
$html_output .= '<td>';
$choices = array(
'1' => __('at least one of the words')
. Util::showHint(
__('Words are separated by a space character (" ").')
),
'2' => __('all words')
. Util::showHint(
__('Words are separated by a space character (" ").')
),
'3' => __('the exact phrase'),
'4' => __('as regular expression') . ' '
. Util::showMySQLDocu('Regexp')
);
// 4th parameter set to true to add line breaks
// 5th parameter set to false to avoid htmlspecialchars() escaping
// in the label since we have some HTML in some labels
$html_output .= Util::getRadioFields(
'criteriaSearchType', $choices, $this->_criteriaSearchType, true, false
);
$html_output .= '</td></tr>';
// displays table names as select options
$html_output .= '<tr>';
$html_output .= '<td class="right vtop">' . __('Inside tables:') . '</td>';
$html_output .= '<td rowspan="2">';
$html_output .= '<select name="criteriaTables[]" size="6"'
. ' multiple="multiple">';
foreach ($this->_tables_names_only as $each_table) {
if (in_array($each_table, $this->_criteriaTables)) {
$is_selected = ' selected="selected"';
} else {
$is_selected = '';
}
$html_output .= '<option value="' . htmlspecialchars($each_table) . '"'
. $is_selected . '>'
. str_replace(' ', '&nbsp;', htmlspecialchars($each_table))
. '</option>';
} // end for
$html_output .= '</select>';
$html_output .= '</td></tr>';
// Displays 'select all' and 'unselect all' links
$alter_select = '<a href="#" '
. 'onclick="setSelectOptions(\'db_search\','
. ' \'criteriaTables[]\', true); return false;">'
. __('Select all') . '</a> &nbsp;/&nbsp;';
$alter_select .= '<a href="#" '
. 'onclick="setSelectOptions(\'db_search\','
. ' \'criteriaTables[]\', false); return false;">'
. __('Unselect all') . '</a>';
$html_output .= '<tr><td class="right vbottom">'
. $alter_select . '</td></tr>';
// Inputbox for column name entry
$html_output .= '<tr>';
$html_output .= '<td class="right">' . __('Inside column:') . '</td>';
$html_output .= '<td><input type="text" name="criteriaColumnName" size="60"'
. 'value="'
. (! empty($this->_criteriaColumnName)
? htmlspecialchars($this->_criteriaColumnName)
: '')
. '" /></td>';
$html_output .= '</tr>';
$html_output .= '</table>';
$html_output .= '</fieldset>';
$html_output .= '<fieldset class="tblFooters">';
$html_output .= '<input type="submit" name="submit_search" value="'
. __('Go') . '" id="buttonGo" />';
$html_output .= '</fieldset>';
$html_output .= '</form>';
$html_output .= '<div id="togglesearchformdiv">'
. '<a id="togglesearchformlink"></a></div>';
return $html_output;
}
/**
* Provides div tags for browsing search results and sql query form.
*
* @return string div tags
*/
public function getResultDivs()
{
$html_output = '<!-- These two table-image and table-link elements display'
. ' the table name in browse search results -->';
$html_output .= '<div id="table-info">';
$html_output .= '<a class="item" id="table-link" ></a>';
$html_output .= '</div>';
// div for browsing results
$html_output .= '<div id="browse-results">';
$html_output .= '<!-- this browse-results div is used to load the browse'
. ' and delete results in the db search -->';
$html_output .= '</div>';
$html_output .= '<br class="clearfloat" />';
$html_output .= '<div id="sqlqueryform">';
$html_output .= '<!-- this sqlqueryform div is used to load the delete'
. ' form in the db search -->';
$html_output .= '</div>';
$html_output .= '<!-- toggle query box link-->';
$html_output .= '<a id="togglequerybox"></a>';
return $html_output;
}
}

File diff suppressed because it is too large Load Diff

507
#pma/libraries/Error.php Normal file
View File

@ -0,0 +1,507 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds class PMA\libraries\Error
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use Exception;
/**
* a single error
*
* @package PhpMyAdmin
*/
class Error extends Message
{
/**
* Error types
*
* @var array
*/
static public $errortype = array (
0 => 'Internal error',
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parsing Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_DEPRECATED => 'Deprecation Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
);
/**
* Error levels
*
* @var array
*/
static public $errorlevel = array (
0 => 'error',
E_ERROR => 'error',
E_WARNING => 'error',
E_PARSE => 'error',
E_NOTICE => 'notice',
E_CORE_ERROR => 'error',
E_CORE_WARNING => 'error',
E_COMPILE_ERROR => 'error',
E_COMPILE_WARNING => 'error',
E_USER_ERROR => 'error',
E_USER_WARNING => 'error',
E_USER_NOTICE => 'notice',
E_STRICT => 'notice',
E_DEPRECATED => 'notice',
E_RECOVERABLE_ERROR => 'error',
);
/**
* The file in which the error occurred
*
* @var string
*/
protected $file = '';
/**
* The line in which the error occurred
*
* @var integer
*/
protected $line = 0;
/**
* Holds the backtrace for this error
*
* @var array
*/
protected $backtrace = array();
/**
* Hide location of errors
*/
protected $hide_location = false;
/**
* Constructor
*
* @param integer $errno error number
* @param string $errstr error message
* @param string $errfile file
* @param integer $errline line
*/
public function __construct($errno, $errstr, $errfile, $errline)
{
$this->setNumber($errno);
$this->setMessage($errstr, false);
$this->setFile($errfile);
$this->setLine($errline);
$backtrace = debug_backtrace();
// remove last three calls:
// debug_backtrace(), handleError() and addError()
$backtrace = array_slice($backtrace, 3);
$this->setBacktrace($backtrace);
}
/**
* Process backtrace to avoid path disclossures, objects and so on
*
* @param array $backtrace backtrace
*
* @return array
*/
public static function processBacktrace($backtrace)
{
$result = array();
$members = array('line', 'function', 'class', 'type');
foreach ($backtrace as $idx => $step) {
/* Create new backtrace entry */
$result[$idx] = array();
/* Make path relative */
if (isset($step['file'])) {
$result[$idx]['file'] = Error::relPath($step['file']);
}
/* Store members we want */
foreach ($members as $name) {
if (isset($step[$name])) {
$result[$idx][$name] = $step[$name];
}
}
/* Store simplified args */
if (isset($step['args'])) {
foreach ($step['args'] as $key => $arg) {
$result[$idx]['args'][$key] = Error::getArg($arg, $step['function']);
}
}
}
return $result;
}
/**
* Toggles location hiding
*
* @param boolean $hide Whether to hide
*
* @return void
*/
public function setHideLocation($hide)
{
$this->hide_location = $hide;
}
/**
* sets PMA\libraries\Error::$_backtrace
*
* We don't store full arguments to avoid wakeup or memory problems.
*
* @param array $backtrace backtrace
*
* @return void
*/
public function setBacktrace($backtrace)
{
$this->backtrace = Error::processBacktrace($backtrace);
}
/**
* sets PMA\libraries\Error::$_line
*
* @param integer $line the line
*
* @return void
*/
public function setLine($line)
{
$this->line = $line;
}
/**
* sets PMA\libraries\Error::$_file
*
* @param string $file the file
*
* @return void
*/
public function setFile($file)
{
$this->file = Error::relPath($file);
}
/**
* returns unique PMA\libraries\Error::$hash, if not exists it will be created
*
* @return string PMA\libraries\Error::$hash
*/
public function getHash()
{
try {
$backtrace = serialize($this->getBacktrace());
} catch(Exception $e) {
$backtrace = '';
}
if ($this->hash === null) {
$this->hash = md5(
$this->getNumber() .
$this->getMessage() .
$this->getFile() .
$this->getLine() .
$backtrace
);
}
return $this->hash;
}
/**
* returns PMA\libraries\Error::$_backtrace for first $count frames
* pass $count = -1 to get full backtrace.
* The same can be done by not passing $count at all.
*
* @param integer $count Number of stack frames.
*
* @return array PMA\libraries\Error::$_backtrace
*/
public function getBacktrace($count = -1)
{
if ($count != -1) {
return array_slice($this->backtrace, 0, $count);
}
return $this->backtrace;
}
/**
* returns PMA\libraries\Error::$file
*
* @return string PMA\libraries\Error::$file
*/
public function getFile()
{
return $this->file;
}
/**
* returns PMA\libraries\Error::$line
*
* @return integer PMA\libraries\Error::$line
*/
public function getLine()
{
return $this->line;
}
/**
* returns type of error
*
* @return string type of error
*/
public function getType()
{
return Error::$errortype[$this->getNumber()];
}
/**
* returns level of error
*
* @return string level of error
*/
public function getLevel()
{
return Error::$errorlevel[$this->getNumber()];
}
/**
* returns title prepared for HTML Title-Tag
*
* @return string HTML escaped and truncated title
*/
public function getHtmlTitle()
{
return htmlspecialchars(
mb_substr($this->getTitle(), 0, 100)
);
}
/**
* returns title for error
*
* @return string
*/
public function getTitle()
{
return $this->getType() . ': ' . $this->getMessage();
}
/**
* Get HTML backtrace
*
* @return string
*/
public function getBacktraceDisplay()
{
return Error::formatBacktrace(
$this->getBacktrace(),
"<br />\n",
"<br />\n"
);
}
/**
* return formatted backtrace field
*
* @param array $backtrace Backtrace data
* @param string $separator Arguments separator to use
* @param string $lines Lines separator to use
*
* @return string formatted backtrace
*/
public static function formatBacktrace($backtrace, $separator, $lines)
{
$retval = '';
foreach ($backtrace as $step) {
if (isset($step['file']) && isset($step['line'])) {
$retval .= Error::relPath($step['file'])
. '#' . $step['line'] . ': ';
}
if (isset($step['class'])) {
$retval .= $step['class'] . $step['type'];
}
$retval .= Error::getFunctionCall($step, $separator);
$retval .= $lines;
}
return $retval;
}
/**
* Formats function call in a backtrace
*
* @param array $step backtrace step
* @param string $separator Arguments separator to use
*
* @return string
*/
public static function getFunctionCall($step, $separator)
{
$retval = $step['function'] . '(';
if (isset($step['args'])) {
if (count($step['args']) > 1) {
$retval .= $separator;
foreach ($step['args'] as $arg) {
$retval .= "\t";
$retval .= $arg;
$retval .= ',' . $separator;
}
} elseif (count($step['args']) > 0) {
foreach ($step['args'] as $arg) {
$retval .= $arg;
}
}
}
$retval .= ')';
return $retval;
}
/**
* Get a single function argument
*
* if $function is one of include/require
* the $arg is converted to a relative path
*
* @param string $arg argument to process
* @param string $function function name
*
* @return string
*/
public static function getArg($arg, $function)
{
$retval = '';
$include_functions = array(
'include',
'include_once',
'require',
'require_once',
);
$connect_functions = array(
'mysql_connect',
'mysql_pconnect',
'mysqli_connect',
'mysqli_real_connect',
'connect',
'_realConnect'
);
if (in_array($function, $include_functions)) {
$retval .= Error::relPath($arg);
} elseif (in_array($function, $connect_functions)
&& getType($arg) === 'string'
) {
$retval .= getType($arg) . ' ********';
} elseif (is_scalar($arg)) {
$retval .= getType($arg) . ' '
. htmlspecialchars(var_export($arg, true));
} elseif (is_object($arg)) {
$retval .= '<Class:' . get_class($arg) . '>';
} else {
$retval .= getType($arg);
}
return $retval;
}
/**
* Gets the error as string of HTML
*
* @return string
*/
public function getDisplay()
{
$this->isDisplayed(true);
$retval = '<div class="' . $this->getLevel() . '">';
if (! $this->isUserError()) {
$retval .= '<strong>' . $this->getType() . '</strong>';
$retval .= ' in ' . $this->getFile() . '#' . $this->getLine();
$retval .= "<br />\n";
}
$retval .= $this->getMessage();
if (! $this->isUserError()) {
$retval .= "<br />\n";
$retval .= "<br />\n";
$retval .= "<strong>Backtrace</strong><br />\n";
$retval .= "<br />\n";
$retval .= $this->getBacktraceDisplay();
}
$retval .= '</div>';
return $retval;
}
/**
* whether this error is a user error
*
* @return boolean
*/
public function isUserError()
{
return $this->hide_location ||
($this->getNumber() & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE));
}
/**
* return short relative path to phpMyAdmin basedir
*
* prevent path disclosure in error message,
* and make users feel safe to submit error reports
*
* @param string $path path to be shorten
*
* @return string shortened path
*/
public static function relPath($path)
{
$dest = @realpath($path);
/* Probably affected by open_basedir */
if ($dest === FALSE) {
return basename($path);
}
$Ahere = explode(
DIRECTORY_SEPARATOR,
realpath(__DIR__ . DIRECTORY_SEPARATOR . '..')
);
$Adest = explode(DIRECTORY_SEPARATOR, $dest);
$result = '.';
// && count ($Adest)>0 && count($Ahere)>0 )
while (implode(DIRECTORY_SEPARATOR, $Adest) != implode(DIRECTORY_SEPARATOR, $Ahere)) {
if (count($Ahere) > count($Adest)) {
array_pop($Ahere);
$result .= DIRECTORY_SEPARATOR . '..';
} else {
array_pop($Adest);
}
}
$path = $result . str_replace(implode(DIRECTORY_SEPARATOR, $Adest), '', $dest);
return str_replace(
DIRECTORY_SEPARATOR . PATH_SEPARATOR,
DIRECTORY_SEPARATOR,
$path
);
}
}

View File

@ -0,0 +1,580 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds class PMA\libraries\ErrorHandler
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use PMA\libraries\Error;
/**
* handling errors
*
* @package PhpMyAdmin
*/
class ErrorHandler
{
/**
* holds errors to be displayed or reported later ...
*
* @var Error[]
*/
protected $errors = array();
/**
* Hide location of errors
*/
protected $hide_location = false;
/**
* Constructor - set PHP error handler
*
*/
public function __construct()
{
/**
* Do not set ourselves as error handler in case of testsuite.
*
* This behavior is not tested there and breaks other tests as they
* rely on PHPUnit doing it's own error handling which we break here.
*/
if (!defined('TESTSUITE')) {
set_error_handler(array($this, 'handleError'));
}
}
/**
* Destructor
*
* stores errors in session
*
*/
public function __destruct()
{
if (isset($_SESSION)) {
if (! isset($_SESSION['errors'])) {
$_SESSION['errors'] = array();
}
// remember only not displayed errors
foreach ($this->errors as $key => $error) {
/**
* We don't want to store all errors here as it would
* explode user session.
*/
if (count($_SESSION['errors']) >= 10) {
$error = new Error(
0,
__('Too many error messages, some are not displayed.'),
__FILE__,
__LINE__
);
$_SESSION['errors'][$error->getHash()] = $error;
break;
} else if (($error instanceof Error)
&& ! $error->isDisplayed()
) {
$_SESSION['errors'][$key] = $error;
}
}
}
}
/**
* Toggles location hiding
*
* @param boolean $hide Whether to hide
*
* @return void
*/
public function setHideLocation($hide)
{
$this->hide_location = $hide;
}
/**
* returns array with all errors
*
* @param $check bool Whether to check for session errors
*
* @return Error[]
*/
public function getErrors($check=true)
{
if ($check) {
$this->checkSavedErrors();
}
return $this->errors;
}
/**
* returns the errors occurred in the current run only.
* Does not include the errors save din the SESSION
*
* @return Error[]
*/
public function getCurrentErrors()
{
return $this->errors;
}
/**
* Pops recent erros from the storage
*
* @param int $count Old error count
*
* @return Error[]
*/
public function sliceErrors($count)
{
$errors = $this->getErrors(false);
$this->errors = array_splice($errors, 0, $count);
return array_splice($errors, $count);
}
/**
* Error handler - called when errors are triggered/occurred
*
* This calls the addError() function, escaping the error string
* Ignores the errors wherever Error Control Operator (@) is used.
*
* @param integer $errno error number
* @param string $errstr error string
* @param string $errfile error file
* @param integer $errline error line
*
* @return void
*/
public function handleError($errno, $errstr, $errfile, $errline)
{
// check if Error Control Operator (@) was used.
if (error_reporting() == 0) {
return;
}
$this->addError($errstr, $errno, $errfile, $errline, true);
}
/**
* Add an error; can also be called directly (with or without escaping)
*
* The following error types cannot be handled with a user defined function:
* E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR,
* E_COMPILE_WARNING,
* and most of E_STRICT raised in the file where set_error_handler() is called.
*
* Do not use the context parameter as we want to avoid storing the
* complete $GLOBALS inside $_SESSION['errors']
*
* @param string $errstr error string
* @param integer $errno error number
* @param string $errfile error file
* @param integer $errline error line
* @param boolean $escape whether to escape the error string
*
* @return void
*/
public function addError($errstr, $errno, $errfile, $errline, $escape = true)
{
if ($escape) {
$errstr = htmlspecialchars($errstr);
}
// create error object
$error = new Error(
$errno,
$errstr,
$errfile,
$errline
);
$error->setHideLocation($this->hide_location);
// do not repeat errors
$this->errors[$error->getHash()] = $error;
switch ($error->getNumber()) {
case E_STRICT:
case E_DEPRECATED:
case E_NOTICE:
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_RECOVERABLE_ERROR:
/* Avoid rendering BB code in PHP errors */
$error->setBBCode(false);
break;
case E_USER_NOTICE:
case E_USER_WARNING:
case E_USER_ERROR:
// just collect the error
// display is called from outside
break;
case E_ERROR:
case E_PARSE:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
default:
// FATAL error, display it and exit
$this->dispFatalError($error);
exit;
}
}
/**
* trigger a custom error
*
* @param string $errorInfo error message
* @param integer $errorNumber error number
*
* @return void
*/
public function triggerError($errorInfo, $errorNumber = null)
{
// we could also extract file and line from backtrace
// and call handleError() directly
trigger_error($errorInfo, $errorNumber);
}
/**
* display fatal error and exit
*
* @param Error $error the error
*
* @return void
*/
protected function dispFatalError($error)
{
if (! headers_sent()) {
$this->dispPageStart($error);
}
$error->display();
$this->dispPageEnd();
exit;
}
/**
* Displays user errors not displayed
*
* @return void
*/
public function dispUserErrors()
{
echo $this->getDispUserErrors();
}
/**
* Renders user errors not displayed
*
* @return string
*/
public function getDispUserErrors()
{
$retval = '';
foreach ($this->getErrors() as $error) {
if ($error->isUserError() && ! $error->isDisplayed()) {
$retval .= $error->getDisplay();
}
}
return $retval;
}
/**
* display HTML header
*
* @param Error $error the error
*
* @return void
*/
protected function dispPageStart($error = null)
{
Response::getInstance()->disable();
echo '<html><head><title>';
if ($error) {
echo $error->getTitle();
} else {
echo 'phpMyAdmin error reporting page';
}
echo '</title></head>';
}
/**
* display HTML footer
*
* @return void
*/
protected function dispPageEnd()
{
echo '</body></html>';
}
/**
* renders errors not displayed
*
* @return string
*/
public function getDispErrors()
{
// Not sure why but seen in reports.phpmyadmin.net
if (empty($GLOBALS['cfg']['SendErrorReports'])) {
$GLOBALS['cfg']['SendErrorReports'] = 'ask';
}
$retval = '';
// display errors if SendErrorReports is set to 'ask'.
if ($GLOBALS['cfg']['SendErrorReports'] != 'never') {
foreach ($this->getErrors() as $error) {
if ($error instanceof Error) {
if (! $error->isDisplayed()) {
$retval .= $error->getDisplay();
}
} else {
$retval .= var_export($error, true);
}
}
} else {
$retval .= $this->getDispUserErrors();
}
// if preference is not 'never' and
// there are 'actual' errors to be reported
if ($GLOBALS['cfg']['SendErrorReports'] != 'never'
&& $this->countErrors() != $this->countUserErrors()
) {
// add report button.
$retval .= '<form method="post" action="error_report.php"'
. ' id="pma_report_errors_form"';
if ($GLOBALS['cfg']['SendErrorReports'] == 'always') {
// in case of 'always', generate 'invisible' form.
$retval .= ' style="display:none;"';
}
$retval .= '>'
. '<input type="hidden" name="token" value="'
. $_SESSION[' PMA_token ']
. '"/>'
. '<input type="hidden" name="exception_type" value="php"/>'
. '<input type="hidden" name="send_error_report" value="1" />'
. '<input type="submit" value="'
. __('Report')
. '" id="pma_report_errors" class="floatright">'
. '<input type="checkbox" name="always_send"'
. ' id="always_send_checkbox" value="true"/>'
. '<label for="always_send_checkbox">'
. __('Automatically send report next time')
. '</label>';
if ($GLOBALS['cfg']['SendErrorReports'] == 'ask') {
// add ignore buttons
$retval .= '<input type="submit" value="'
. __('Ignore')
. '" id="pma_ignore_errors_bottom" class="floatright">';
}
$retval .= '<input type="submit" value="'
. __('Ignore All')
. '" id="pma_ignore_all_errors_bottom" class="floatright">';
$retval .= '</form>';
}
return $retval;
}
/**
* displays errors not displayed
*
* @return void
*/
public function dispErrors()
{
echo $this->getDispErrors();
}
/**
* look in session for saved errors
*
* @return void
*/
protected function checkSavedErrors()
{
if (isset($_SESSION['errors'])) {
// restore saved errors
foreach ($_SESSION['errors'] as $hash => $error) {
if ($error instanceof Error && ! isset($this->errors[$hash])) {
$this->errors[$hash] = $error;
}
}
//$this->errors = array_merge($_SESSION['errors'], $this->errors);
// delete stored errors
$_SESSION['errors'] = array();
unset($_SESSION['errors']);
}
}
/**
* return count of errors
*
* @param $check bool Whether to check for session errors
*
* @return integer number of errors occurred
*/
public function countErrors($check=true)
{
return count($this->getErrors($check));
}
/**
* return count of user errors
*
* @return integer number of user errors occurred
*/
public function countUserErrors()
{
$count = 0;
if ($this->countErrors()) {
foreach ($this->getErrors() as $error) {
if ($error->isUserError()) {
$count++;
}
}
}
return $count;
}
/**
* whether use errors occurred or not
*
* @return boolean
*/
public function hasUserErrors()
{
return (bool) $this->countUserErrors();
}
/**
* whether errors occurred or not
*
* @return boolean
*/
public function hasErrors()
{
return (bool) $this->countErrors();
}
/**
* number of errors to be displayed
*
* @return integer number of errors to be displayed
*/
public function countDisplayErrors()
{
if ($GLOBALS['cfg']['SendErrorReports'] != 'never') {
return $this->countErrors();
} else {
return $this->countUserErrors();
}
}
/**
* whether there are errors to display or not
*
* @return boolean
*/
public function hasDisplayErrors()
{
return (bool) $this->countDisplayErrors();
}
/**
* Deletes previously stored errors in SESSION.
* Saves current errors in session as previous errors.
* Required to save current errors in case 'ask'
*
* @return void
*/
public function savePreviousErrors()
{
unset($_SESSION['prev_errors']);
$_SESSION['prev_errors'] = $GLOBALS['error_handler']->getCurrentErrors();
}
/**
* Function to check if there are any errors to be prompted.
* Needed because user warnings raised are
* also collected by global error handler.
* This distinguishes between the actual errors
* and user errors raised to warn user.
*
*@return boolean true if there are errors to be "prompted", false otherwise
*/
public function hasErrorsForPrompt()
{
return (
$GLOBALS['cfg']['SendErrorReports'] != 'never'
&& $this->countErrors() != $this->countUserErrors()
);
}
/**
* Function to report all the collected php errors.
* Must be called at the end of each script
* by the $GLOBALS['error_handler'] only.
*
* @return void
*/
public function reportErrors()
{
// if there're no actual errors,
if (!$this->hasErrors()
|| $this->countErrors() == $this->countUserErrors()
) {
// then simply return.
return;
}
// Delete all the prev_errors in session & store new prev_errors in session
$this->savePreviousErrors();
$response = Response::getInstance();
$jsCode = '';
if ($GLOBALS['cfg']['SendErrorReports'] == 'always') {
if ($response->isAjax()) {
// set flag for automatic report submission.
$response->addJSON('_sendErrorAlways', '1');
} else {
// send the error reports asynchronously & without asking user
$jsCode .= '$("#pma_report_errors_form").submit();'
. 'PMA_ajaxShowMessage(
PMA_messages["phpErrorsBeingSubmitted"], false
);';
// js code to appropriate focusing,
$jsCode .= '$("html, body").animate({
scrollTop:$(document).height()
}, "slow");';
}
} elseif ($GLOBALS['cfg']['SendErrorReports'] == 'ask') {
//ask user whether to submit errors or not.
if (!$response->isAjax()) {
// js code to show appropriate msgs, event binding & focusing.
$jsCode = 'PMA_ajaxShowMessage(PMA_messages["phpErrorsFound"]);'
. '$("#pma_ignore_errors_popup").bind("click", function() {
PMA_ignorePhpErrors()
});'
. '$("#pma_ignore_all_errors_popup").bind("click",
function() {
PMA_ignorePhpErrors(false)
});'
. '$("#pma_ignore_errors_bottom").bind("click", function(e) {
e.preventDefaulut();
PMA_ignorePhpErrors()
});'
. '$("#pma_ignore_all_errors_bottom").bind("click",
function(e) {
e.preventDefault();
PMA_ignorePhpErrors(false)
});'
. '$("html, body").animate({
scrollTop:$(document).height()
}, "slow");';
}
}
// The errors are already sent from the response.
// Just focus on errors division upon load event.
$response->getFooter()->getScripts()->addCode($jsCode);
}
}

745
#pma/libraries/File.php Normal file
View File

@ -0,0 +1,745 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* file upload functions
*
* @package PMA\libraries
*/
namespace PMA\libraries;
/**
* File wrapper class
*
* @todo when uploading a file into a blob field, should we also consider using
* chunks like in import? UPDATE `table` SET `field` = `field` + [chunk]
*
* @package PMA\libraries
*/
class File
{
/**
* @var string the temporary file name
* @access protected
*/
var $_name = null;
/**
* @var string the content
* @access protected
*/
var $_content = null;
/**
* @var string the error message
* @access protected
*/
var $_error_message = '';
/**
* @var bool whether the file is temporary or not
* @access protected
*/
var $_is_temp = false;
/**
* @var string type of compression
* @access protected
*/
var $_compression = null;
/**
* @var integer
*/
var $_offset = 0;
/**
* @var integer size of chunk to read with every step
*/
var $_chunk_size = 32768;
/**
* @var resource file handle
*/
var $_handle = null;
/**
* @var boolean whether to decompress content before returning
*/
var $_decompress = false;
/**
* @var string charset of file
*/
var $_charset = null;
/**
* constructor
*
* @param boolean|string $name file name or false
*
* @access public
*/
public function __construct($name = false)
{
if ($name && is_string($name)) {
$this->setName($name);
}
}
/**
* destructor
*
* @see File::cleanUp()
* @access public
*/
public function __destruct()
{
$this->cleanUp();
}
/**
* deletes file if it is temporary, usually from a moved upload file
*
* @access public
* @return boolean success
*/
public function cleanUp()
{
if ($this->isTemp()) {
return $this->delete();
}
return true;
}
/**
* deletes the file
*
* @access public
* @return boolean success
*/
public function delete()
{
return unlink($this->getName());
}
/**
* checks or sets the temp flag for this file
* file objects with temp flags are deleted with object destruction
*
* @param boolean $is_temp sets the temp flag
*
* @return boolean File::$_is_temp
* @access public
*/
public function isTemp($is_temp = null)
{
if (null !== $is_temp) {
$this->_is_temp = (bool) $is_temp;
}
return $this->_is_temp;
}
/**
* accessor
*
* @param string $name file name
*
* @return void
* @access public
*/
public function setName($name)
{
$this->_name = trim($name);
}
/**
* Gets file content
*
* @return string|false the binary file content as a string,
* or false if no content
*
* @access public
*/
public function getContent()
{
if (null === $this->_content) {
if ($this->isUploaded() && ! $this->checkUploadedFile()) {
return false;
}
if (! $this->isReadable()) {
return false;
}
if (function_exists('file_get_contents')) {
$this->_content = file_get_contents($this->getName());
} elseif ($size = filesize($this->getName())) {
$this->_content = fread(fopen($this->getName(), 'rb'), $size);
}
}
return '0x' . bin2hex($this->_content);
}
/**
* Whether file is uploaded.
*
* @access public
*
* @return bool
*/
public function isUploaded()
{
return is_uploaded_file($this->getName());
}
/**
* accessor
*
* @access public
* @return string File::$_name
*/
public function getName()
{
return $this->_name;
}
/**
* Initializes object from uploaded file.
*
* @param string $name name of file uploaded
*
* @return boolean success
* @access public
*/
public function setUploadedFile($name)
{
$this->setName($name);
if (! $this->isUploaded()) {
$this->setName(null);
$this->_error_message = __('File was not an uploaded file.');
return false;
}
return true;
}
/**
* Loads uploaded file from table change request.
*
* @param string $key the md5 hash of the column name
* @param string $rownumber number of row to process
*
* @return boolean success
* @access public
*/
public function setUploadedFromTblChangeRequest($key, $rownumber)
{
if (! isset($_FILES['fields_upload'])
|| empty($_FILES['fields_upload']['name']['multi_edit'][$rownumber][$key])
) {
return false;
}
$file = File::fetchUploadedFromTblChangeRequestMultiple(
$_FILES['fields_upload'],
$rownumber,
$key
);
// check for file upload errors
switch ($file['error']) {
// we do not use the PHP constants here cause not all constants
// are defined in all versions of PHP - but the correct constants names
// are given as comment
case 0: //UPLOAD_ERR_OK:
return $this->setUploadedFile($file['tmp_name']);
case 4: //UPLOAD_ERR_NO_FILE:
break;
case 1: //UPLOAD_ERR_INI_SIZE:
$this->_error_message = __(
'The uploaded file exceeds the upload_max_filesize directive in '
. 'php.ini.'
);
break;
case 2: //UPLOAD_ERR_FORM_SIZE:
$this->_error_message = __(
'The uploaded file exceeds the MAX_FILE_SIZE directive that was '
. 'specified in the HTML form.'
);
break;
case 3: //UPLOAD_ERR_PARTIAL:
$this->_error_message = __(
'The uploaded file was only partially uploaded.'
);
break;
case 6: //UPLOAD_ERR_NO_TMP_DIR:
$this->_error_message = __('Missing a temporary folder.');
break;
case 7: //UPLOAD_ERR_CANT_WRITE:
$this->_error_message = __('Failed to write file to disk.');
break;
case 8: //UPLOAD_ERR_EXTENSION:
$this->_error_message = __('File upload stopped by extension.');
break;
default:
$this->_error_message = __('Unknown error in file upload.');
} // end switch
return false;
}
/**
* strips some dimension from the multi-dimensional array from $_FILES
*
* <code>
* $file['name']['multi_edit'][$rownumber][$key] = [value]
* $file['type']['multi_edit'][$rownumber][$key] = [value]
* $file['size']['multi_edit'][$rownumber][$key] = [value]
* $file['tmp_name']['multi_edit'][$rownumber][$key] = [value]
* $file['error']['multi_edit'][$rownumber][$key] = [value]
*
* // becomes:
*
* $file['name'] = [value]
* $file['type'] = [value]
* $file['size'] = [value]
* $file['tmp_name'] = [value]
* $file['error'] = [value]
* </code>
*
* @param array $file the array
* @param string $rownumber number of row to process
* @param string $key key to process
*
* @return array
* @access public
* @static
*/
public function fetchUploadedFromTblChangeRequestMultiple(
$file, $rownumber, $key
) {
$new_file = array(
'name' => $file['name']['multi_edit'][$rownumber][$key],
'type' => $file['type']['multi_edit'][$rownumber][$key],
'size' => $file['size']['multi_edit'][$rownumber][$key],
'tmp_name' => $file['tmp_name']['multi_edit'][$rownumber][$key],
'error' => $file['error']['multi_edit'][$rownumber][$key],
);
return $new_file;
}
/**
* sets the name if the file to the one selected in the tbl_change form
*
* @param string $key the md5 hash of the column name
* @param string $rownumber number of row to process
*
* @return boolean success
* @access public
*/
public function setSelectedFromTblChangeRequest($key, $rownumber = null)
{
if (! empty($_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key])
&& is_string($_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key])
) {
// ... whether with multiple rows ...
return $this->setLocalSelectedFile(
$_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key]
);
} else {
return false;
}
}
/**
* Returns possible error message.
*
* @access public
* @return string error message
*/
public function getError()
{
return $this->_error_message;
}
/**
* Checks whether there was any error.
*
* @access public
* @return boolean whether an error occurred or not
*/
public function isError()
{
return ! empty($this->_error_message);
}
/**
* checks the superglobals provided if the tbl_change form is submitted
* and uses the submitted/selected file
*
* @param string $key the md5 hash of the column name
* @param string $rownumber number of row to process
*
* @return boolean success
* @access public
*/
public function checkTblChangeForm($key, $rownumber)
{
if ($this->setUploadedFromTblChangeRequest($key, $rownumber)) {
// well done ...
$this->_error_message = '';
return true;
} elseif ($this->setSelectedFromTblChangeRequest($key, $rownumber)) {
// well done ...
$this->_error_message = '';
return true;
}
// all failed, whether just no file uploaded/selected or an error
return false;
}
/**
* Sets named file to be read from UploadDir.
*
* @param string $name file name
*
* @return boolean success
* @access public
*/
public function setLocalSelectedFile($name)
{
if (empty($GLOBALS['cfg']['UploadDir'])) {
return false;
}
$this->setName(
Util::userDir($GLOBALS['cfg']['UploadDir']) . PMA_securePath($name)
);
if (@is_link($this->getName())) {
$this->_error_message = __('File is a symbolic link');
$this->setName(null);
return false;
}
if (! $this->isReadable()) {
$this->_error_message = __('File could not be read!');
$this->setName(null);
return false;
}
return true;
}
/**
* Checks whether file can be read.
*
* @access public
* @return boolean whether the file is readable or not
*/
public function isReadable()
{
// suppress warnings from being displayed, but not from being logged
// any file access outside of open_basedir will issue a warning
return @is_readable($this->getName());
}
/**
* If we are on a server with open_basedir, we must move the file
* before opening it. The FAQ 1.11 explains how to create the "./tmp"
* directory - if needed
*
* @todo move check of $cfg['TempDir'] into Config?
* @access public
* @return boolean whether uploaded file is fine or not
*/
public function checkUploadedFile()
{
if ($this->isReadable()) {
return true;
}
if (empty($GLOBALS['cfg']['TempDir'])
|| ! @is_writable($GLOBALS['cfg']['TempDir'])
) {
// cannot create directory or access, point user to FAQ 1.11
$this->_error_message = __(
'Error moving the uploaded file, see [doc@faq1-11]FAQ 1.11[/doc].'
);
return false;
}
$new_file_to_upload = tempnam(
realpath($GLOBALS['cfg']['TempDir']),
basename($this->getName())
);
// suppress warnings from being displayed, but not from being logged
// any file access outside of open_basedir will issue a warning
ob_start();
$move_uploaded_file_result = move_uploaded_file(
$this->getName(),
$new_file_to_upload
);
ob_end_clean();
if (! $move_uploaded_file_result) {
$this->_error_message = __('Error while moving uploaded file.');
return false;
}
$this->setName($new_file_to_upload);
$this->isTemp(true);
if (! $this->isReadable()) {
$this->_error_message = __('Cannot read uploaded file.');
return false;
}
return true;
}
/**
* Detects what compression the file uses
*
* @todo move file read part into readChunk() or getChunk()
* @todo add support for compression plugins
* @access protected
* @return string|false false on error, otherwise string MIME type of
* compression, none for none
*/
protected function detectCompression()
{
// suppress warnings from being displayed, but not from being logged
// f.e. any file access outside of open_basedir will issue a warning
ob_start();
$file = fopen($this->getName(), 'rb');
ob_end_clean();
if (! $file) {
$this->_error_message = __('File could not be read!');
return false;
}
/**
* @todo
* get registered plugins for file compression
foreach (PMA_getPlugins($type = 'compression') as $plugin) {
if ($plugin['classname']::canHandle($this->getName())) {
$this->setCompressionPlugin($plugin);
break;
}
}
*/
$this->_compression = Util::getCompressionMimeType($file);
return $this->_compression;
}
/**
* Sets whether the content should be decompressed before returned
*
* @param boolean $decompress whether to decompress
*
* @return void
*/
public function setDecompressContent($decompress)
{
$this->_decompress = (bool) $decompress;
}
/**
* Returns the file handle
*
* @return resource file handle
*/
public function getHandle()
{
if (null === $this->_handle) {
$this->open();
}
return $this->_handle;
}
/**
* Sets the file handle
*
* @param object $handle file handle
*
* @return void
*/
public function setHandle($handle)
{
$this->_handle = $handle;
}
/**
* Sets error message for unsupported compression.
*
* @return void
*/
public function errorUnsupported()
{
$this->_error_message = sprintf(
__(
'You attempted to load file with unsupported compression (%s). '
. 'Either support for it is not implemented or disabled by your '
. 'configuration.'
),
$this->getCompression()
);
}
/**
* Attempts to open the file.
*
* @return bool
*/
public function open()
{
if (! $this->_decompress) {
$this->_handle = @fopen($this->getName(), 'r');
}
switch ($this->getCompression()) {
case false:
return false;
case 'application/bzip2':
if ($GLOBALS['cfg']['BZipDump'] && @function_exists('bzopen')) {
$this->_handle = @bzopen($this->getName(), 'r');
} else {
$this->errorUnsupported();
return false;
}
break;
case 'application/gzip':
if ($GLOBALS['cfg']['GZipDump'] && @function_exists('gzopen')) {
$this->_handle = @gzopen($this->getName(), 'r');
} else {
$this->errorUnsupported();
return false;
}
break;
case 'application/zip':
if ($GLOBALS['cfg']['ZipDump'] && @function_exists('zip_open')) {
include_once './libraries/zip_extension.lib.php';
$result = PMA_getZipContents($this->getName());
if (! empty($result['error'])) {
$this->_error_message = Message::rawError($result['error']);
return false;
}
unset($result);
} else {
$this->errorUnsupported();
return false;
}
break;
case 'none':
$this->_handle = @fopen($this->getName(), 'r');
break;
default:
$this->errorUnsupported();
return false;
}
return true;
}
/**
* Returns the character set of the file
*
* @return string character set of the file
*/
public function getCharset()
{
return $this->_charset;
}
/**
* Sets the character set of the file
*
* @param string $charset character set of the file
*
* @return void
*/
public function setCharset($charset)
{
$this->_charset = $charset;
}
/**
* Returns compression used by file.
*
* @return string MIME type of compression, none for none
* @access public
*/
public function getCompression()
{
if (null === $this->_compression) {
return $this->detectCompression();
}
return $this->_compression;
}
/**
* Returns the offset
*
* @return integer the offset
*/
public function getOffset()
{
return $this->_offset;
}
/**
* Returns the chunk size
*
* @return integer the chunk size
*/
public function getChunkSize()
{
return $this->_chunk_size;
}
/**
* Sets the chunk size
*
* @param integer $chunk_size the chunk size
*
* @return void
*/
public function setChunkSize($chunk_size)
{
$this->_chunk_size = (int) $chunk_size;
}
/**
* Returns the length of the content in the file
*
* @return integer the length of the file content
*/
public function getContentLength()
{
return mb_strlen($this->_content);
}
/**
* Returns whether the end of the file has been reached
*
* @return boolean whether the end of the file has been reached
*/
public function eof()
{
if ($this->getHandle()) {
return feof($this->getHandle());
} else {
return ($this->getOffset() >= $this->getContentLength());
}
}
}

142
#pma/libraries/Font.php Normal file
View File

@ -0,0 +1,142 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Class with Font related methods.
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Class with Font related methods.
*
* @package PhpMyAdmin
*/
class Font
{
/**
* Get list with characters and the corresponding width modifiers.
*
* @return array with characters and corresponding width modifier
* @access public
*/
public static function getCharLists()
{
// list of characters and their width modifiers
$charLists = array();
//ijl
$charLists[] = array("chars" => array("i", "j", "l"), "modifier" => 0.23);
//f
$charLists[] = array("chars" => array("f"), "modifier" => 0.27);
//tI
$charLists[] = array("chars" => array("t", "I"), "modifier" => 0.28);
//r
$charLists[] = array("chars" => array("r"), "modifier" => 0.34);
//1
$charLists[] = array("chars" => array("1"), "modifier" => 0.49);
//cksvxyzJ
$charLists[] = array(
"chars" => array("c", "k", "s", "v", "x", "y", "z", "J"),
"modifier" => 0.5
);
//abdeghnopquL023456789
$charLists[] = array(
"chars" => array(
"a", "b", "d", "e", "g", "h", "n", "o", "p", "q", "u", "L",
"0", "2", "3", "4", "5", "6", "7", "8", "9"
),
"modifier" => 0.56
);
//FTZ
$charLists[] = array("chars" => array("F", "T", "Z"), "modifier" => 0.61);
//ABEKPSVXY
$charLists[] = array(
"chars" => array("A", "B", "E", "K", "P", "S", "V", "X", "Y"),
"modifier" => 0.67
);
//wCDHNRU
$charLists[] = array(
"chars" => array("w", "C", "D", "H", "N", "R", "U"),
"modifier" => 0.73
);
//GOQ
$charLists[] = array("chars" => array("G", "O", "Q"), "modifier" => 0.78);
//mM
$charLists[] = array("chars" => array("m", "M"), "modifier" => 0.84);
//W
$charLists[] = array("chars" => array("W"), "modifier" => 0.95);
//" "
$charLists[] = array("chars" => array(" "), "modifier" => 0.28);
return $charLists;
}
/**
* Get width of string/text
*
* The text element width is calculated depending on font name
* and font size.
*
* @param string $text string of which the width will be calculated
* @param string $font name of the font like Arial,sans-serif etc
* @param integer $fontSize size of font
* @param array $charLists list of characters and their width modifiers
*
* @return integer width of the text
* @access public
*/
public static function getStringWidth($text, $font, $fontSize, $charLists = null)
{
if (empty($charLists) || !is_array($charLists)
|| !isset($charLists[0]["chars"]) || !is_array($charLists[0]["chars"])
|| !isset($charLists[0]["modifier"])
) {
$charLists = self::getCharLists();
}
/*
* Start by counting the width, giving each character a modifying value
*/
$count = 0;
foreach ($charLists as $charList) {
$count += ((mb_strlen($text)
- mb_strlen(str_replace($charList["chars"], "", $text))
) * $charList["modifier"]);
}
$text = str_replace(" ", "", $text);//remove the " "'s
//all other chars
$count = $count
+ (mb_strlen(preg_replace("/[a-z0-9]/i", "", $text)) * 0.3);
$modifier = 1;
$font = mb_strtolower($font);
switch ($font) {
/*
* no modifier for arial and sans-serif
*/
case 'arial':
case 'sans-serif':
break;
/*
* .92 modifier for time, serif, brushscriptstd, and californian fb
*/
case 'times':
case 'serif':
case 'brushscriptstd':
case 'californian fb':
$modifier = .92;
break;
/*
* 1.23 modifier for broadway
*/
case 'broadway':
$modifier = 1.23;
break;
}
$textWidth = $count * $fontSize;
return (int)ceil($textWidth * $modifier);
}
}

373
#pma/libraries/Footer.php Normal file
View File

@ -0,0 +1,373 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Used to render the footer of PMA's pages
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use Traversable;
/**
* Class used to output the footer
*
* @package PhpMyAdmin
*/
class Footer
{
/**
* Scripts instance
*
* @access private
* @var Scripts
*/
private $_scripts;
/**
* Whether we are servicing an ajax request.
* We can't simply use $GLOBALS['is_ajax_request']
* here since it may have not been initialised yet.
*
* @access private
* @var bool
*/
private $_isAjax;
/**
* Whether to only close the BODY and HTML tags
* or also include scripts, errors and links
*
* @access private
* @var bool
*/
private $_isMinimal;
/**
* Whether to display anything
*
* @access private
* @var bool
*/
private $_isEnabled;
/**
* Creates a new class instance
*/
public function __construct()
{
$this->_isEnabled = true;
$this->_scripts = new Scripts();
$this->_isMinimal = false;
}
/**
* Returns the message for demo server to error messages
*
* @return string
*/
private function _getDemoMessage()
{
$message = '<a href="/">' . __('phpMyAdmin Demo Server') . '</a>: ';
if (file_exists('./revision-info.php')) {
include './revision-info.php';
$message .= sprintf(
__('Currently running Git revision %1$s from the %2$s branch.'),
'<a target="_blank" rel="noopener noreferrer" href="' . $repobase . $fullrevision . '">'
. $revision . '</a>',
'<a target="_blank" rel="noopener noreferrer" href="' . $repobranchbase . $branch . '">'
. $branch . '</a>'
);
} else {
$message .= __('Git information missing!');
}
return Message::notice($message)->getDisplay();
}
/**
* Remove recursions and iterator objects from an object
*
* @param object|array &$object Object to clean
* @param array $stack Stack used to keep track of recursion,
* need not be passed for the first time
*
* @return object Reference passed object
*/
private static function _removeRecursion(&$object, $stack = array())
{
if ((is_object($object) || is_array($object)) && $object) {
if ($object instanceof Traversable) {
$object = "***ITERATOR***";
} else if (!in_array($object, $stack, true)) {
$stack[] = $object;
foreach ($object as &$subobject) {
self::_removeRecursion($subobject, $stack);
}
} else {
$object = "***RECURSION***";
}
}
return $object;
}
/**
* Renders the debug messages
*
* @return string
*/
public function getDebugMessage()
{
$retval = '\'null\'';
if ($GLOBALS['cfg']['DBG']['sql']
&& empty($_REQUEST['no_debug'])
&& !empty($_SESSION['debug'])
) {
// Remove recursions and iterators from $_SESSION['debug']
self::_removeRecursion($_SESSION['debug']);
$retval = JSON_encode($_SESSION['debug']);
$_SESSION['debug'] = array();
return json_last_error() ? '\'false\'' : $retval;
}
$_SESSION['debug'] = array();
return $retval;
}
/**
* Returns the url of the current page
*
* @param string|null $encode See PMA_URL_getCommon()
*
* @return string
*/
public function getSelfUrl($encode = 'html')
{
$db = ! empty($GLOBALS['db']) ? $GLOBALS['db'] : '';
$table = ! empty($GLOBALS['table']) ? $GLOBALS['table'] : '';
$target = ! empty($_REQUEST['target']) ? $_REQUEST['target'] : '';
$params = array(
'db' => $db,
'table' => $table,
'server' => $GLOBALS['server'],
'target' => $target
);
// needed for server privileges tabs
if (isset($_REQUEST['viewing_mode'])
&& in_array($_REQUEST['viewing_mode'], array('server', 'db', 'table'))
) {
$params['viewing_mode'] = $_REQUEST['viewing_mode'];
}
/*
* @todo coming from server_privileges.php, here $db is not set,
* add the following condition below when that is fixed
* && $_REQUEST['checkprivsdb'] == $db
*/
if (isset($_REQUEST['checkprivsdb'])
) {
$params['checkprivsdb'] = $_REQUEST['checkprivsdb'];
}
/*
* @todo coming from server_privileges.php, here $table is not set,
* add the following condition below when that is fixed
* && $_REQUEST['checkprivstable'] == $table
*/
if (isset($_REQUEST['checkprivstable'])
) {
$params['checkprivstable'] = $_REQUEST['checkprivstable'];
}
if (isset($_REQUEST['single_table'])
&& in_array($_REQUEST['single_table'], array(true, false))
) {
$params['single_table'] = $_REQUEST['single_table'];
}
return basename(PMA_getenv('SCRIPT_NAME')) . PMA_URL_getCommon(
$params,
$encode
);
}
/**
* Renders the link to open a new page
*
* @param string $url The url of the page
*
* @return string
*/
private function _getSelfLink($url)
{
$retval = '';
$retval .= '<div id="selflink" class="print_ignore">';
$retval .= '<a href="' . $url . '"'
. ' title="' . __('Open new phpMyAdmin window') . '" target="_blank" rel="noopener noreferrer">';
if (Util::showIcons('TabsMode')) {
$retval .= Util::getImage(
'window-new.png',
__('Open new phpMyAdmin window')
);
} else {
$retval .= __('Open new phpMyAdmin window');
}
$retval .= '</a>';
$retval .= '</div>';
return $retval;
}
/**
* Renders the link to open a new page
*
* @return string
*/
public function getErrorMessages()
{
$retval = '';
if ($GLOBALS['error_handler']->hasDisplayErrors()) {
$retval .= $GLOBALS['error_handler']->getDispErrors();
}
/**
* Report php errors
*/
$GLOBALS['error_handler']->reportErrors();
return $retval;
}
/**
* Saves query in history
*
* @return void
*/
private function _setHistory()
{
if (! PMA_isValid($_REQUEST['no_history'])
&& empty($GLOBALS['error_message'])
&& ! empty($GLOBALS['sql_query'])
&& (isset($GLOBALS['dbi'])
&& ($GLOBALS['dbi']->getLink()
|| isset($GLOBALS['controllink'])
&& $GLOBALS['controllink']))
) {
PMA_setHistory(
PMA_ifSetOr($GLOBALS['db'], ''),
PMA_ifSetOr($GLOBALS['table'], ''),
$GLOBALS['cfg']['Server']['user'],
$GLOBALS['sql_query']
);
}
}
/**
* Disables the rendering of the footer
*
* @return void
*/
public function disable()
{
$this->_isEnabled = false;
}
/**
* Set the ajax flag to indicate whether
* we are servicing an ajax request
*
* @param bool $isAjax Whether we are servicing an ajax request
*
* @return void
*/
public function setAjax($isAjax)
{
$this->_isAjax = (boolean) $isAjax;
}
/**
* Turn on minimal display mode
*
* @return void
*/
public function setMinimal()
{
$this->_isMinimal = true;
}
/**
* Returns the Scripts object
*
* @return Scripts object
*/
public function getScripts()
{
return $this->_scripts;
}
/**
* Renders the footer
*
* @return string
*/
public function getDisplay()
{
$retval = '';
$this->_setHistory();
if ($this->_isEnabled) {
if (! $this->_isAjax) {
$retval .= "</div>";
}
if (! $this->_isAjax && ! $this->_isMinimal) {
if (PMA_getenv('SCRIPT_NAME')
&& empty($_POST)
&& empty($GLOBALS['checked_special'])
&& ! $this->_isAjax
) {
$url = $this->getSelfUrl('unencoded');
$header = Response::getInstance()->getHeader();
$scripts = $header->getScripts()->getFiles();
$menuHash = $header->getMenu()->getHash();
// prime the client-side cache
$this->_scripts->addCode(
sprintf(
'if (! (history && history.pushState)) '
. 'PMA_MicroHistory.primer = {'
. ' url: "%s",'
. ' scripts: %s,'
. ' menuHash: "%s"'
. '};',
PMA_escapeJsString($url),
json_encode($scripts),
PMA_escapeJsString($menuHash)
)
);
}
if (PMA_getenv('SCRIPT_NAME')
&& ! $this->_isAjax
) {
$url = $this->getSelfUrl();
$retval .= $this->_getSelfLink($url);
}
$this->_scripts->addCode(
'var debugSQLInfo = ' . $this->getDebugMessage() . ';'
);
$retval .= '<div class="clearfloat" id="pma_errors">';
$retval .= $this->getErrorMessages();
$retval .= '</div>';
$retval .= $this->_scripts->getDisplay();
if ($GLOBALS['cfg']['DBG']['demo']) {
$retval .= '<div id="pma_demo">';
$retval .= $this->_getDemoMessage();
$retval .= '</div>';
}
// Include possible custom footers
if (file_exists(CUSTOM_FOOTER_FILE)) {
$retval .= '<div id="pma_footer">';
ob_start();
include CUSTOM_FOOTER_FILE;
$retval .= ob_get_contents();
ob_end_clean();
$retval .= '</div>';
}
}
if (! $this->_isAjax) {
$retval .= "</body></html>";
}
}
return $retval;
}
}

813
#pma/libraries/Header.php Normal file
View File

@ -0,0 +1,813 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Used to render the header of PMA's pages
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use PMA\libraries\navigation\Navigation;
require_once 'libraries/js_escape.lib.php';
require_once 'libraries/url_generating.lib.php';
/**
* Class used to output the HTTP and HTML headers
*
* @package PhpMyAdmin
*/
class Header
{
/**
* Scripts instance
*
* @access private
* @var Scripts
*/
private $_scripts;
/**
* PMA\libraries\Console instance
*
* @access private
* @var Console
*/
private $_console;
/**
* Menu instance
*
* @access private
* @var Menu
*/
private $_menu;
/**
* Whether to offer the option of importing user settings
*
* @access private
* @var bool
*/
private $_userprefsOfferImport;
/**
* The page title
*
* @access private
* @var string
*/
private $_title;
/**
* The value for the id attribute for the body tag
*
* @access private
* @var string
*/
private $_bodyId;
/**
* Whether to show the top menu
*
* @access private
* @var bool
*/
private $_menuEnabled;
/**
* Whether to show the warnings
*
* @access private
* @var bool
*/
private $_warningsEnabled;
/**
* Whether the page is in 'print view' mode
*
* @access private
* @var bool
*/
private $_isPrintView;
/**
* Whether we are servicing an ajax request.
* We can't simply use $GLOBALS['is_ajax_request']
* here since it may have not been initialised yet.
*
* @access private
* @var bool
*/
private $_isAjax;
/**
* Whether to display anything
*
* @access private
* @var bool
*/
private $_isEnabled;
/**
* Whether the HTTP headers (and possibly some HTML)
* have already been sent to the browser
*
* @access private
* @var bool
*/
private $_headerIsSent;
/**
* Creates a new class instance
*/
public function __construct()
{
$this->_isEnabled = true;
$this->_isAjax = false;
$this->_bodyId = '';
$this->_title = '';
$this->_console = new Console();
$db = ! empty($GLOBALS['db']) ? $GLOBALS['db'] : '';
$table = ! empty($GLOBALS['table']) ? $GLOBALS['table'] : '';
$this->_menu = new Menu(
$GLOBALS['server'],
$db,
$table
);
$this->_menuEnabled = true;
$this->_warningsEnabled = true;
$this->_isPrintView = false;
$this->_scripts = new Scripts();
$this->_addDefaultScripts();
$this->_headerIsSent = false;
// if database storage for user preferences is transient,
// offer to load exported settings from localStorage
// (detection will be done in JavaScript)
$this->_userprefsOfferImport = false;
if ($GLOBALS['PMA_Config']->get('user_preferences') == 'session'
&& ! isset($_SESSION['userprefs_autoload'])
) {
$this->_userprefsOfferImport = true;
}
}
/**
* Loads common scripts
*
* @return void
*/
private function _addDefaultScripts()
{
// Localised strings
$params = array('lang' => $GLOBALS['lang']);
if (isset($GLOBALS['db'])) {
$params['db'] = $GLOBALS['db'];
}
$this->_scripts->addFile('jquery/jquery-2.1.4.min.js');
$this->_scripts->addFile(
'whitelist.php' . PMA_URL_getCommon($params), false, true
);
$this->_scripts->addFile('sprintf.js');
$this->_scripts->addFile('ajax.js');
$this->_scripts->addFile('keyhandler.js');
$this->_scripts->addFile('jquery/jquery-ui-1.11.4.min.js');
$this->_scripts->addFile('jquery/jquery.cookie.js');
$this->_scripts->addFile('jquery/jquery.mousewheel.js');
$this->_scripts->addFile('jquery/jquery.event.drag-2.2.js');
$this->_scripts->addFile('jquery/jquery-ui-timepicker-addon.js');
$this->_scripts->addFile('jquery/jquery.ba-hashchange-1.3.js');
$this->_scripts->addFile('jquery/jquery.debounce-1.0.5.js');
$this->_scripts->addFile('menu-resizer.js');
// Cross-framing protection
if ($GLOBALS['cfg']['AllowThirdPartyFraming'] === false) {
$this->_scripts->addFile('cross_framing_protection.js');
}
$this->_scripts->addFile('rte.js');
if ($GLOBALS['cfg']['SendErrorReports'] !== 'never') {
$this->_scripts->addFile('tracekit/tracekit.js');
$this->_scripts->addFile('error_report.js');
}
// Here would not be a good place to add CodeMirror because
// the user preferences have not been merged at this point
$this->_scripts->addFile('messages.php' . PMA_URL_getCommon($params));
// Append the theme id to this url to invalidate
// the cache on a theme change. Though this might be
// unavailable for fatal errors.
if (isset($_SESSION['PMA_Theme'])) {
$theme_id = urlencode($_SESSION['PMA_Theme']->getId());
} else {
$theme_id = 'default';
}
$this->_scripts->addFile(
'get_image.js.php?theme=' . $theme_id
);
$this->_scripts->addFile('config.js');
$this->_scripts->addFile('doclinks.js');
$this->_scripts->addFile('functions.js');
$this->_scripts->addFile('navigation.js');
$this->_scripts->addFile('indexes.js');
$this->_scripts->addFile('common.js');
$this->_scripts->addFile('page_settings.js');
$this->_scripts->addCode($this->getJsParamsCode());
}
/**
* Returns, as an array, a list of parameters
* used on the client side
*
* @return array
*/
public function getJsParams()
{
$db = ! empty($GLOBALS['db']) ? $GLOBALS['db'] : '';
$table = ! empty($GLOBALS['table']) ? $GLOBALS['table'] : '';
$pftext = ! empty($_SESSION['tmpval']['pftext'])
? $_SESSION['tmpval']['pftext'] : '';
// not sure when this happens, but it happens
if (! isset($GLOBALS['collation_connection'])) {
$GLOBALS['collation_connection'] = 'utf8_general_ci';
}
$params = array(
'common_query' => PMA_URL_getCommon(array(), 'text'),
'opendb_url' => Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabDatabase'], 'database'
),
'safari_browser' => PMA_USR_BROWSER_AGENT == 'SAFARI' ? 1 : 0,
'collation_connection' => $GLOBALS['collation_connection'],
'lang' => $GLOBALS['lang'],
'server' => $GLOBALS['server'],
'table' => $table,
'db' => $db,
'token' => $_SESSION[' PMA_token '],
'text_dir' => $GLOBALS['text_dir'],
'show_databases_navigation_as_tree' => $GLOBALS['cfg']['ShowDatabasesNavigationAsTree'],
'pma_text_default_tab' => Util::getTitleForTarget(
$GLOBALS['cfg']['DefaultTabTable']
),
'pma_text_left_default_tab' => Util::getTitleForTarget(
$GLOBALS['cfg']['NavigationTreeDefaultTabTable']
),
'pma_text_left_default_tab2' => Util::getTitleForTarget(
$GLOBALS['cfg']['NavigationTreeDefaultTabTable2']
),
'LimitChars' => $GLOBALS['cfg']['LimitChars'],
'pftext' => $pftext,
'confirm' => $GLOBALS['cfg']['Confirm'],
'LoginCookieValidity' => $GLOBALS['cfg']['LoginCookieValidity'],
'logged_in' => isset($GLOBALS['userlink']) ? true : false,
'PMA_VERSION' => PMA_VERSION
);
if (isset($GLOBALS['cfg']['Server'])
&& isset($GLOBALS['cfg']['Server']['auth_type'])
) {
$params['auth_type'] = $GLOBALS['cfg']['Server']['auth_type'];
}
return $params;
}
/**
* Returns, as a string, a list of parameters
* used on the client side
*
* @return string
*/
public function getJsParamsCode()
{
$params = $this->getJsParams();
foreach ($params as $key => $value) {
$params[$key] = $key . ':"' . PMA_escapeJsString($value) . '"';
}
return 'PMA_commonParams.setAll({' . implode(',', $params) . '});';
}
/**
* Disables the rendering of the header
*
* @return void
*/
public function disable()
{
$this->_isEnabled = false;
}
/**
* Set the ajax flag to indicate whether
* we are servicing an ajax request
*
* @param bool $isAjax Whether we are servicing an ajax request
*
* @return void
*/
public function setAjax($isAjax)
{
$this->_isAjax = (boolean) $isAjax;
$this->_console->setAjax($isAjax);
}
/**
* Returns the Scripts object
*
* @return Scripts object
*/
public function getScripts()
{
return $this->_scripts;
}
/**
* Returns the Menu object
*
* @return Menu object
*/
public function getMenu()
{
return $this->_menu;
}
/**
* Setter for the ID attribute in the BODY tag
*
* @param string $id Value for the ID attribute
*
* @return void
*/
public function setBodyId($id)
{
$this->_bodyId = htmlspecialchars($id);
}
/**
* Setter for the title of the page
*
* @param string $title New title
*
* @return void
*/
public function setTitle($title)
{
$this->_title = htmlspecialchars($title);
}
/**
* Disables the display of the top menu
*
* @return void
*/
public function disableMenuAndConsole()
{
$this->_menuEnabled = false;
$this->_console->disable();
}
/**
* Disables the display of the top menu
*
* @return void
*/
public function disableWarnings()
{
$this->_warningsEnabled = false;
}
/**
* Turns on 'print view' mode
*
* @return void
*/
public function enablePrintView()
{
$this->disableMenuAndConsole();
$this->setTitle(__('Print view') . ' - phpMyAdmin ' . PMA_VERSION);
$this->_isPrintView = true;
}
/**
* Generates the header
*
* @return string The header
*/
public function getDisplay()
{
$retval = '';
if (! $this->_headerIsSent) {
if (! $this->_isAjax && $this->_isEnabled) {
$this->sendHttpHeaders();
$retval .= $this->_getHtmlStart();
$retval .= $this->_getMetaTags();
$retval .= $this->_getLinkTags();
$retval .= $this->getTitleTag();
// The user preferences have been merged at this point
// so we can conditionally add CodeMirror
if ($GLOBALS['cfg']['CodemirrorEnable']) {
$this->_scripts->addFile('codemirror/lib/codemirror.js');
$this->_scripts->addFile('codemirror/mode/sql/sql.js');
$this->_scripts->addFile('codemirror/addon/runmode/runmode.js');
$this->_scripts->addFile('codemirror/addon/hint/show-hint.js');
$this->_scripts->addFile('codemirror/addon/hint/sql-hint.js');
if ($GLOBALS['cfg']['LintEnable']) {
$this->_scripts->addFile('codemirror/addon/lint/lint.js');
$this->_scripts->addFile(
'codemirror/addon/lint/sql-lint.js'
);
}
}
$this->_scripts->addCode(
'ConsoleEnterExecutes='
. ($GLOBALS['cfg']['ConsoleEnterExecutes'] ? 'true' : 'false')
);
$this->_scripts->addFiles($this->_console->getScripts());
if ($this->_userprefsOfferImport) {
$this->_scripts->addFile('config.js');
}
$retval .= $this->_scripts->getDisplay();
$retval .= '<noscript>';
$retval .= '<style>html{display:block}</style>';
$retval .= '</noscript>';
$retval .= $this->_getBodyStart();
if ($this->_menuEnabled && $GLOBALS['server'] > 0) {
$nav = new Navigation();
$retval .= $nav->getDisplay();
}
// Include possible custom headers
if (file_exists(CUSTOM_HEADER_FILE)) {
$retval .= '<div id="pma_header">';
ob_start();
include CUSTOM_HEADER_FILE;
$retval .= ob_get_contents();
ob_end_clean();
$retval .= '</div>';
}
// offer to load user preferences from localStorage
if ($this->_userprefsOfferImport) {
include_once './libraries/user_preferences.lib.php';
$retval .= PMA_userprefsAutoloadGetHeader();
}
// pass configuration for hint tooltip display
// (to be used by PMA_tooltip() in js/functions.js)
if (! $GLOBALS['cfg']['ShowHint']) {
$retval .= '<span id="no_hint" class="hide"></span>';
}
$retval .= $this->_getWarnings();
if ($this->_menuEnabled && $GLOBALS['server'] > 0) {
$retval .= $this->_menu->getDisplay();
$retval .= '<span id="page_nav_icons">';
$retval .= '<span id="lock_page_icon"></span>';
$retval .= '<span id="page_settings_icon">'
. Util::getImage(
's_cog.png',
__('Page-related settings')
)
. '</span>';
$retval .= sprintf(
'<a id="goto_pagetop" href="#">%s</a>',
Util::getImage(
's_top.png',
__('Click on the bar to scroll to top of page')
)
);
$retval .= '</span>';
}
$retval .= $this->_console->getDisplay();
$retval .= '<div id="page_content">';
$retval .= $this->getMessage();
}
if ($this->_isEnabled && empty($_REQUEST['recent_table'])) {
$retval .= $this->_addRecentTable(
$GLOBALS['db'],
$GLOBALS['table']
);
}
}
return $retval;
}
/**
* Returns the message to be displayed at the top of
* the page, including the executed SQL query, if any.
*
* @return string
*/
public function getMessage()
{
$retval = '';
$message = '';
if (! empty($GLOBALS['message'])) {
$message = $GLOBALS['message'];
unset($GLOBALS['message']);
} else if (! empty($_REQUEST['message'])) {
$message = $_REQUEST['message'];
}
if (! empty($message)) {
if (isset($GLOBALS['buffer_message'])) {
$buffer_message = $GLOBALS['buffer_message'];
}
$retval .= Util::getMessage($message);
if (isset($buffer_message)) {
$GLOBALS['buffer_message'] = $buffer_message;
}
}
return $retval;
}
/**
* Sends out the HTTP headers
*
* @return void
*/
public function sendHttpHeaders()
{
if (defined('TESTSUITE') && ! defined('PMA_TEST_HEADERS')) {
return;
}
$map_tile_urls = ' *.tile.openstreetmap.org';
/**
* Sends http headers
*/
$GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT';
if (!empty($GLOBALS['cfg']['CaptchaLoginPrivateKey'])
&& !empty($GLOBALS['cfg']['CaptchaLoginPublicKey'])
) {
$captcha_url
= ' https://apis.google.com https://www.google.com/recaptcha/'
. ' https://www.gstatic.com/recaptcha/ https://ssl.gstatic.com/ ';
} else {
$captcha_url = '';
}
/* Prevent against ClickJacking by disabling framing */
if (! $GLOBALS['cfg']['AllowThirdPartyFraming']) {
header(
'X-Frame-Options: DENY'
);
}
header(
"Content-Security-Policy: default-src 'self' "
. $captcha_url
. $GLOBALS['cfg']['CSPAllow'] . ';'
. "script-src 'self' 'unsafe-inline' 'unsafe-eval' "
. $captcha_url
. $GLOBALS['cfg']['CSPAllow'] . ';'
. ";"
. "style-src 'self' 'unsafe-inline' "
. $captcha_url
. $GLOBALS['cfg']['CSPAllow']
. ";"
. "referrer no-referrer;"
. "img-src 'self' data: "
. $GLOBALS['cfg']['CSPAllow']
. $map_tile_urls
. $captcha_url
. ";"
);
header(
"X-Content-Security-Policy: default-src 'self' "
. $captcha_url
. $GLOBALS['cfg']['CSPAllow'] . ';'
. "options inline-script eval-script;"
. "referrer no-referrer;"
. "img-src 'self' data: "
. $GLOBALS['cfg']['CSPAllow']
. $map_tile_urls
. $captcha_url
. ";"
);
header(
"X-WebKit-CSP: default-src 'self' "
. $captcha_url
. $GLOBALS['cfg']['CSPAllow'] . ';'
. "script-src 'self' "
. $captcha_url
. $GLOBALS['cfg']['CSPAllow']
. " 'unsafe-inline' 'unsafe-eval';"
. "referrer no-referrer;"
. "style-src 'self' 'unsafe-inline' "
. $captcha_url
. ';'
. "img-src 'self' data: "
. $GLOBALS['cfg']['CSPAllow']
. $map_tile_urls
. $captcha_url
. ";"
);
// Re-enable possible disabled XSS filters
// see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
header(
'X-XSS-Protection: 1; mode=block'
);
// "nosniff", prevents Internet Explorer and Google Chrome from MIME-sniffing
// a response away from the declared content-type
// see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
header(
'X-Content-Type-Options: nosniff'
);
// Adobe cross-domain-policies
// see http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
header(
'X-Permitted-Cross-Domain-Policies: none'
);
// Robots meta tag
// see https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
header(
'X-Robots-Tag: noindex, nofollow'
);
PMA_noCacheHeader();
if (! defined('IS_TRANSFORMATION_WRAPPER')) {
// Define the charset to be used
header('Content-Type: text/html; charset=utf-8');
}
$this->_headerIsSent = true;
}
/**
* Returns the DOCTYPE and the start HTML tag
*
* @return string DOCTYPE and HTML tags
*/
private function _getHtmlStart()
{
$lang = $GLOBALS['lang'];
$dir = $GLOBALS['text_dir'];
$retval = "<!DOCTYPE HTML>";
$retval .= "<html lang='$lang' dir='$dir' class='";
$retval .= mb_strtolower(PMA_USR_BROWSER_AGENT) . " ";
$retval .= mb_strtolower(PMA_USR_BROWSER_AGENT)
. intval(PMA_USR_BROWSER_VER) . "'>";
$retval .= '<head>';
return $retval;
}
/**
* Returns the META tags
*
* @return string the META tags
*/
private function _getMetaTags()
{
$retval = '<meta charset="utf-8" />';
$retval .= '<meta name="referrer" content="no-referrer" />';
$retval .= '<meta name="robots" content="noindex,nofollow" />';
$retval .= '<meta http-equiv="X-UA-Compatible" content="IE=Edge">';
if (! $GLOBALS['cfg']['AllowThirdPartyFraming']) {
$retval .= '<style id="cfs-style">html{display: none;}</style>';
}
return $retval;
}
/**
* Returns the LINK tags for the favicon and the stylesheets
*
* @return string the LINK tags
*/
private function _getLinkTags()
{
$retval = '<link rel="icon" href="favicon.ico" '
. 'type="image/x-icon" />'
. '<link rel="shortcut icon" href="favicon.ico" '
. 'type="image/x-icon" />';
// stylesheets
$basedir = defined('PMA_PATH_TO_BASEDIR') ? PMA_PATH_TO_BASEDIR : '';
$theme_id = $GLOBALS['PMA_Config']->getThemeUniqueValue();
$theme_path = $GLOBALS['pmaThemePath'];
$v = self::getVersionParameter();
if ($this->_isPrintView) {
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $basedir . 'print.css?' . $v . '" />';
} else {
// load jQuery's CSS prior to our theme's CSS, to let the theme
// override jQuery's CSS
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $theme_path . '/jquery/jquery-ui-1.11.4.css" />';
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $basedir . 'js/codemirror/lib/codemirror.css?' . $v . '" />';
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $basedir . 'js/codemirror/addon/hint/show-hint.css?' . $v . '" />';
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $basedir . 'js/codemirror/addon/lint/lint.css?' . $v . '" />';
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $basedir . 'phpmyadmin.css.php?'
. 'nocache=' . $theme_id . $GLOBALS['text_dir'] . '" />';
// load Print view's CSS last, so that it overrides all other CSS while
// 'printing'
$retval .= '<link rel="stylesheet" type="text/css" href="'
. $theme_path . '/css/printview.css?' . $v . '" media="print" id="printcss"/>';
}
return $retval;
}
/**
* Returns the TITLE tag
*
* @return string the TITLE tag
*/
public function getTitleTag()
{
$retval = "<title>";
$retval .= $this->_getPageTitle();
$retval .= "</title>";
return $retval;
}
/**
* If the page is missing the title, this function
* will set it to something reasonable
*
* @return string
*/
private function _getPageTitle()
{
if (empty($this->_title)) {
if ($GLOBALS['server'] > 0) {
if (! empty($GLOBALS['table'])) {
$temp_title = $GLOBALS['cfg']['TitleTable'];
} else if (! empty($GLOBALS['db'])) {
$temp_title = $GLOBALS['cfg']['TitleDatabase'];
} elseif (! empty($GLOBALS['cfg']['Server']['host'])) {
$temp_title = $GLOBALS['cfg']['TitleServer'];
} else {
$temp_title = $GLOBALS['cfg']['TitleDefault'];
}
$this->_title = htmlspecialchars(
Util::expandUserString($temp_title)
);
} else {
$this->_title = 'phpMyAdmin';
}
}
return $this->_title;
}
/**
* Returns the close tag to the HEAD
* and the start tag for the BODY
*
* @return string HEAD and BODY tags
*/
private function _getBodyStart()
{
$retval = "</head><body";
if (! empty($this->_bodyId)) {
$retval .= " id='" . $this->_bodyId . "'";
}
$retval .= ">";
return $retval;
}
/**
* Returns some warnings to be displayed at the top of the page
*
* @return string The warnings
*/
private function _getWarnings()
{
$retval = '';
if ($this->_warningsEnabled) {
$retval .= "<noscript>";
$retval .= Message::error(
__("Javascript must be enabled past this point!")
)->getDisplay();
$retval .= "</noscript>";
}
return $retval;
}
/**
* Add recently used table and reload the navigation.
*
* @param string $db Database name where the table is located.
* @param string $table The table name
*
* @return string
*/
private function _addRecentTable($db, $table)
{
$retval = '';
if ($this->_menuEnabled
&& mb_strlen($table)
&& $GLOBALS['cfg']['NumRecentTables'] > 0
) {
$tmp_result = RecentFavoriteTable::getInstance('recent')
->add($db, $table);
if ($tmp_result === true) {
$retval = RecentFavoriteTable::getHtmlUpdateRecentTables();
} else {
$error = $tmp_result;
$retval = $error->getDisplay();
}
}
return $retval;
}
/**
* Returns the phpMyAdmin version to be appended to the url to avoid caching
* between versions
*
* @return string urlenocded pma version as a parameter
*/
public static function getVersionParameter()
{
return "v=" . urlencode(PMA_VERSION);
}
}

887
#pma/libraries/Index.php Normal file
View File

@ -0,0 +1,887 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* holds the database index class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Index manipulation class
*
* @package PhpMyAdmin
* @since phpMyAdmin 3.0.0
*/
class Index
{
const PRIMARY = 1;
const UNIQUE = 2;
const INDEX = 4;
const SPATIAL = 8;
const FULLTEXT = 16;
/**
* Class-wide storage container for indexes (caching, singleton)
*
* @var array
*/
private static $_registry = array();
/**
* @var string The name of the schema
*/
private $_schema = '';
/**
* @var string The name of the table
*/
private $_table = '';
/**
* @var string The name of the index
*/
private $_name = '';
/**
* Columns in index
*
* @var array
*/
private $_columns = array();
/**
* The index method used (BTREE, HASH, RTREE).
*
* @var string
*/
private $_type = '';
/**
* The index choice (PRIMARY, UNIQUE, INDEX, SPATIAL, FULLTEXT)
*
* @var string
*/
private $_choice = '';
/**
* Various remarks.
*
* @var string
*/
private $_remarks = '';
/**
* Any comment provided for the index with a COMMENT attribute when the
* index was created.
*
* @var string
*/
private $_comment = '';
/**
* @var integer 0 if the index cannot contain duplicates, 1 if it can.
*/
private $_non_unique = 0;
/**
* Indicates how the key is packed. NULL if it is not.
*
* @var string
*/
private $_packed = null;
/**
* Block size for the index
*
* @var int
*/
private $_key_block_size = null;
/**
* Parser option for the index
*
* @var string
*/
private $_parser = null;
/**
* Constructor
*
* @param array $params parameters
*/
public function __construct($params = array())
{
$this->set($params);
}
/**
* Creates(if not already created) and returns the corresponding Index object
*
* @param string $schema database name
* @param string $table table name
* @param string $index_name index name
*
* @return Index corresponding Index object
*/
static public function singleton($schema, $table, $index_name = '')
{
Index::_loadIndexes($table, $schema);
if (! isset(Index::$_registry[$schema][$table][$index_name])) {
$index = new Index;
if (mb_strlen($index_name)) {
$index->setName($index_name);
Index::$_registry[$schema][$table][$index->getName()] = $index;
}
return $index;
} else {
return Index::$_registry[$schema][$table][$index_name];
}
}
/**
* returns an array with all indexes from the given table
*
* @param string $table table
* @param string $schema schema
*
* @return Index[] array of indexes
*/
static public function getFromTable($table, $schema)
{
Index::_loadIndexes($table, $schema);
if (isset(Index::$_registry[$schema][$table])) {
return Index::$_registry[$schema][$table];
} else {
return array();
}
}
/**
* Returns an array with all indexes from the given table of the requested types
*
* @param string $table table
* @param string $schema schema
* @param int $choices choices
*
* @return Index[] array of indexes
*/
static public function getFromTableByChoice($table, $schema, $choices = 31)
{
$indexes = array();
foreach (self::getFromTable($table, $schema) as $index) {
if (($choices & Index::PRIMARY)
&& $index->getChoice() == 'PRIMARY'
) {
$indexes[] = $index;
}
if (($choices & Index::UNIQUE)
&& $index->getChoice() == 'UNIQUE'
) {
$indexes[] = $index;
}
if (($choices & Index::INDEX)
&& $index->getChoice() == 'INDEX'
) {
$indexes[] = $index;
}
if (($choices & Index::SPATIAL)
&& $index->getChoice() == 'SPATIAL'
) {
$indexes[] = $index;
}
if (($choices & Index::FULLTEXT)
&& $index->getChoice() == 'FULLTEXT'
) {
$indexes[] = $index;
}
}
return $indexes;
}
/**
* return primary if set, false otherwise
*
* @param string $table table
* @param string $schema schema
*
* @return mixed primary index or false if no one exists
*/
static public function getPrimary($table, $schema)
{
Index::_loadIndexes($table, $schema);
if (isset(Index::$_registry[$schema][$table]['PRIMARY'])) {
return Index::$_registry[$schema][$table]['PRIMARY'];
} else {
return false;
}
}
/**
* Load index data for table
*
* @param string $table table
* @param string $schema schema
*
* @return boolean whether loading was successful
*/
static private function _loadIndexes($table, $schema)
{
if (isset(Index::$_registry[$schema][$table])) {
return true;
}
$_raw_indexes = $GLOBALS['dbi']->getTableIndexes($schema, $table);
foreach ($_raw_indexes as $_each_index) {
$_each_index['Schema'] = $schema;
$keyName = $_each_index['Key_name'];
if (! isset(Index::$_registry[$schema][$table][$keyName])) {
$key = new Index($_each_index);
Index::$_registry[$schema][$table][$keyName] = $key;
} else {
$key = Index::$_registry[$schema][$table][$keyName];
}
$key->addColumn($_each_index);
}
return true;
}
/**
* Add column to index
*
* @param array $params column params
*
* @return void
*/
public function addColumn($params)
{
if (isset($params['Column_name'])
&& mb_strlen($params['Column_name'])
) {
$this->_columns[$params['Column_name']] = new IndexColumn($params);
}
}
/**
* Adds a list of columns to the index
*
* @param array $columns array containing details about the columns
*
* @return void
*/
public function addColumns($columns)
{
$_columns = array();
if (isset($columns['names'])) {
// coming from form
// $columns[names][]
// $columns[sub_parts][]
foreach ($columns['names'] as $key => $name) {
$sub_part = isset($columns['sub_parts'][$key])
? $columns['sub_parts'][$key] : '';
$_columns[] = array(
'Column_name' => $name,
'Sub_part' => $sub_part,
);
}
} else {
// coming from SHOW INDEXES
// $columns[][name]
// $columns[][sub_part]
// ...
$_columns = $columns;
}
foreach ($_columns as $column) {
$this->addColumn($column);
}
}
/**
* Returns true if $column indexed in this index
*
* @param string $column the column
*
* @return boolean true if $column indexed in this index
*/
public function hasColumn($column)
{
return isset($this->_columns[$column]);
}
/**
* Sets index details
*
* @param array $params index details
*
* @return void
*/
public function set($params)
{
if (isset($params['columns'])) {
$this->addColumns($params['columns']);
}
if (isset($params['Schema'])) {
$this->_schema = $params['Schema'];
}
if (isset($params['Table'])) {
$this->_table = $params['Table'];
}
if (isset($params['Key_name'])) {
$this->_name = $params['Key_name'];
}
if (isset($params['Index_type'])) {
$this->_type = $params['Index_type'];
}
if (isset($params['Comment'])) {
$this->_remarks = $params['Comment'];
}
if (isset($params['Index_comment'])) {
$this->_comment = $params['Index_comment'];
}
if (isset($params['Non_unique'])) {
$this->_non_unique = $params['Non_unique'];
}
if (isset($params['Packed'])) {
$this->_packed = $params['Packed'];
}
if (isset($params['Index_choice'])) {
$this->_choice = $params['Index_choice'];
} else {
if ('PRIMARY' == $this->_name) {
$this->_choice = 'PRIMARY';
} elseif ('FULLTEXT' == $this->_type) {
$this->_choice = 'FULLTEXT';
$this->_type = '';
} elseif ('SPATIAL' == $this->_type) {
$this->_choice = 'SPATIAL';
$this->_type = '';
} elseif ('0' == $this->_non_unique) {
$this->_choice = 'UNIQUE';
} else {
$this->_choice = 'INDEX';
}
}
if (isset($params['Key_block_size'])) {
$this->_key_block_size = $params['Key_block_size'];
}
if (isset($params['Parser'])) {
$this->_parser = $params['Parser'];
}
}
/**
* Returns the number of columns of the index
*
* @return integer the number of the columns
*/
public function getColumnCount()
{
return count($this->_columns);
}
/**
* Returns the index comment
*
* @return string index comment
*/
public function getComment()
{
return $this->_comment;
}
/**
* Returns index remarks
*
* @return string index remarks
*/
public function getRemarks()
{
return $this->_remarks;
}
/**
* Return the key block size
*
* @return number
*/
public function getKeyBlockSize()
{
return $this->_key_block_size;
}
/**
* Return the parser
*
* @return string
*/
public function getParser()
{
return $this->_parser;
}
/**
* Returns concatenated remarks and comment
*
* @return string concatenated remarks and comment
*/
public function getComments()
{
$comments = $this->getRemarks();
if (mb_strlen($comments)) {
$comments .= "\n";
}
$comments .= $this->getComment();
return $comments;
}
/**
* Returns index type (BTREE, HASH, RTREE)
*
* @return string index type
*/
public function getType()
{
return $this->_type;
}
/**
* Returns index choice (PRIMARY, UNIQUE, INDEX, SPATIAL, FULLTEXT)
*
* @return string index choice
*/
public function getChoice()
{
return $this->_choice;
}
/**
* Return a list of all index choices
*
* @return string[] index choices
*/
static public function getIndexChoices()
{
return array(
'PRIMARY',
'INDEX',
'UNIQUE',
'SPATIAL',
'FULLTEXT',
);
}
/**
* Returns a lit of all index types
*
* @return string[] index types
*/
static public function getIndexTypes()
{
return array(
'BTREE',
'HASH'
);
}
/**
* Returns HTML for the index choice selector
*
* @param boolean $edit_table whether this is table editing
*
* @return string HTML for the index choice selector
*/
public function generateIndexChoiceSelector($edit_table)
{
$html_options = '<select name="index[Index_choice]"'
. ' id="select_index_choice" '
. ($edit_table ? 'disabled="disabled"' : '') . '>';
foreach (Index::getIndexChoices() as $each_index_choice) {
if ($each_index_choice === 'PRIMARY'
&& $this->_choice !== 'PRIMARY'
&& Index::getPrimary($this->_table, $this->_schema)
) {
// skip PRIMARY if there is already one in the table
continue;
}
$html_options .= '<option value="' . $each_index_choice . '"'
. (($this->_choice == $each_index_choice)
? ' selected="selected"'
: '')
. '>' . $each_index_choice . '</option>' . "\n";
}
$html_options .= '</select>';
return $html_options;
}
/**
* Returns HTML for the index type selector
*
* @return string HTML for the index type selector
*/
public function generateIndexTypeSelector()
{
$types = array("" => "--");
foreach (Index::getIndexTypes() as $type) {
$types[$type] = $type;
}
return Util::getDropdown(
"index[Index_type]", $types,
$this->_type, "select_index_type"
);
}
/**
* Returns how the index is packed
*
* @return string how the index is packed
*/
public function getPacked()
{
return $this->_packed;
}
/**
* Returns 'No'/false if the index is not packed,
* how the index is packed if packed
*
* @param boolean $as_text whether to output should be in text
*
* @return mixed how index is packed
*/
public function isPacked($as_text = false)
{
if ($as_text) {
$r = array(
'0' => __('No'),
'1' => __('Yes'),
);
} else {
$r = array(
'0' => false,
'1' => true,
);
}
if (null === $this->_packed) {
return $r[0];
}
return $this->_packed;
}
/**
* Returns integer 0 if the index cannot contain duplicates, 1 if it can
*
* @return integer 0 if the index cannot contain duplicates, 1 if it can
*/
public function getNonUnique()
{
return $this->_non_unique;
}
/**
* Returns whether the index is a 'Unique' index
*
* @param boolean $as_text whether to output should be in text
*
* @return mixed whether the index is a 'Unique' index
*/
public function isUnique($as_text = false)
{
if ($as_text) {
$r = array(
'0' => __('Yes'),
'1' => __('No'),
);
} else {
$r = array(
'0' => true,
'1' => false,
);
}
return $r[$this->_non_unique];
}
/**
* Returns the name of the index
*
* @return string the name of the index
*/
public function getName()
{
return $this->_name;
}
/**
* Sets the name of the index
*
* @param string $name index name
*
* @return void
*/
public function setName($name)
{
$this->_name = (string) $name;
}
/**
* Returns the columns of the index
*
* @return IndexColumn[] the columns of the index
*/
public function getColumns()
{
return $this->_columns;
}
/**
* Show index data
*
* @param string $table The table name
* @param string $schema The schema name
* @param boolean $print_mode Whether the output is for the print mode
*
* @return string HTML for showing index
*
* @access public
*/
static public function getHtmlForIndexes($table, $schema, $print_mode = false)
{
$indexes = Index::getFromTable($table, $schema);
$no_indexes_class = count($indexes) > 0 ? ' hide' : '';
$no_indexes = "<div class='no_indexes_defined$no_indexes_class'>";
$no_indexes .= Message::notice(__('No index defined!'))->getDisplay();
$no_indexes .= '</div>';
if (! $print_mode) {
$r = '<fieldset class="index_info">';
$r .= '<legend id="index_header">' . __('Indexes');
$r .= Util::showMySQLDocu('optimizing-database-structure');
$r .= '</legend>';
$r .= $no_indexes;
if (count($indexes) < 1) {
$r .= '</fieldset>';
return $r;
}
$r .= Index::findDuplicates($table, $schema);
} else {
$r = '<h3>' . __('Indexes') . '</h3>';
$r .= $no_indexes;
if (count($indexes) < 1) {
return $r;
}
}
$r .= '<table id="table_index">';
$r .= '<thead>';
$r .= '<tr>';
if (! $print_mode) {
$r .= '<th colspan="2" class="print_ignore">' . __('Action') . '</th>';
}
$r .= '<th>' . __('Keyname') . '</th>';
$r .= '<th>' . __('Type') . '</th>';
$r .= '<th>' . __('Unique') . '</th>';
$r .= '<th>' . __('Packed') . '</th>';
$r .= '<th>' . __('Column') . '</th>';
$r .= '<th>' . __('Cardinality') . '</th>';
$r .= '<th>' . __('Collation') . '</th>';
$r .= '<th>' . __('Null') . '</th>';
$r .= '<th>' . __('Comment') . '</th>';
$r .= '</tr>';
$r .= '</thead>';
$r .= '<tbody>';
$odd_row = true;
foreach ($indexes as $index) {
$row_span = ' rowspan="' . $index->getColumnCount() . '" ';
$r .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
if (! $print_mode) {
$this_params = $GLOBALS['url_params'];
$this_params['index'] = $index->getName();
$r .= '<td class="edit_index print_ignore';
$r .= ' ajax';
$r .= '" ' . $row_span . '>'
. ' <a class="';
$r .= 'ajax';
$r .= '" href="tbl_indexes.php' . PMA_URL_getCommon($this_params)
. '">' . Util::getIcon('b_edit.png', __('Edit')) . '</a>'
. '</td>' . "\n";
$this_params = $GLOBALS['url_params'];
if ($index->getName() == 'PRIMARY') {
$this_params['sql_query'] = 'ALTER TABLE '
. Util::backquote($table)
. ' DROP PRIMARY KEY;';
$this_params['message_to_show']
= __('The primary key has been dropped.');
$js_msg = PMA_jsFormat(
'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'
);
} else {
$this_params['sql_query'] = 'ALTER TABLE '
. Util::backquote($table) . ' DROP INDEX '
. Util::backquote($index->getName()) . ';';
$this_params['message_to_show'] = sprintf(
__('Index %s has been dropped.'), htmlspecialchars($index->getName())
);
$js_msg = PMA_jsFormat(
'ALTER TABLE ' . $table . ' DROP INDEX '
. $index->getName() . ';'
);
}
$r .= '<td ' . $row_span . ' class="print_ignore">';
$r .= '<input type="hidden" class="drop_primary_key_index_msg"'
. ' value="' . $js_msg . '" />';
$r .= ' <a class="drop_primary_key_index_anchor';
$r .= ' ajax';
$r .= '" href="sql.php' . PMA_URL_getCommon($this_params)
. '" >'
. Util::getIcon('b_drop.png', __('Drop')) . '</a>'
. '</td>' . "\n";
}
if (! $print_mode) {
$r .= '<th ' . $row_span . '>'
. htmlspecialchars($index->getName())
. '</th>';
} else {
$r .= '<td ' . $row_span . '>'
. htmlspecialchars($index->getName())
. '</td>';
}
$r .= '<td ' . $row_span . '>';
$type = $index->getType();
if (! empty($type)) {
$r .= htmlspecialchars($type);
} else {
$r .= htmlspecialchars($index->getChoice());
}
$r .= '</td>';
$r .= '<td ' . $row_span . '>' . $index->isUnique(true) . '</td>';
$r .= '<td ' . $row_span . '>' . $index->isPacked(true) . '</td>';
foreach ($index->getColumns() as $column) {
if ($column->getSeqInIndex() > 1) {
$r .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
}
$r .= '<td>' . htmlspecialchars($column->getName());
if ($column->getSubPart()) {
$r .= ' (' . htmlspecialchars($column->getSubPart()) . ')';
}
$r .= '</td>';
$r .= '<td>'
. htmlspecialchars($column->getCardinality())
. '</td>';
$r .= '<td>'
. htmlspecialchars($column->getCollation())
. '</td>';
$r .= '<td>'
. htmlspecialchars($column->getNull(true))
. '</td>';
if ($column->getSeqInIndex() == 1
) {
$r .= '<td ' . $row_span . '>'
. htmlspecialchars($index->getComments()) . '</td>';
}
$r .= '</tr>';
} // end foreach $index['Sequences']
$odd_row = ! $odd_row;
} // end while
$r .= '</tbody>';
$r .= '</table>';
if (! $print_mode) {
$r .= '</fieldset>';
}
return $r;
}
/**
* Gets the properties in an array for comparison purposes
*
* @return array an array containing the properties of the index
*/
public function getCompareData()
{
$data = array(
// 'Non_unique' => $this->_non_unique,
'Packed' => $this->_packed,
'Index_choice' => $this->_choice,
);
foreach ($this->_columns as $column) {
$data['columns'][] = $column->getCompareData();
}
return $data;
}
/**
* Function to check over array of indexes and look for common problems
*
* @param string $table table name
* @param string $schema schema name
*
* @return string Output HTML
* @access public
*/
static public function findDuplicates($table, $schema)
{
$indexes = Index::getFromTable($table, $schema);
$output = '';
// count($indexes) < 2:
// there is no need to check if there less than two indexes
if (count($indexes) < 2) {
return $output;
}
// remove last index from stack and ...
while ($while_index = array_pop($indexes)) {
// ... compare with every remaining index in stack
foreach ($indexes as $each_index) {
if ($each_index->getCompareData() !== $while_index->getCompareData()
) {
continue;
}
// did not find any difference
// so it makes no sense to have this two equal indexes
$message = Message::notice(
__(
'The indexes %1$s and %2$s seem to be equal and one of them '
. 'could possibly be removed.'
)
);
$message->addParam($each_index->getName());
$message->addParam($while_index->getName());
$output .= $message->getDisplay();
// there is no need to check any further indexes if we have already
// found that this one has a duplicate
continue 2;
}
}
return $output;
}
}

View File

@ -0,0 +1,186 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* holds the database index columns class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Index column wrapper
*
* @package PhpMyAdmin
*/
class IndexColumn
{
/**
* @var string The column name
*/
private $_name = '';
/**
* @var integer The column sequence number in the index, starting with 1.
*/
private $_seq_in_index = 1;
/**
* @var string How the column is sorted in the index. “A” (Ascending) or
* NULL (Not sorted)
*/
private $_collation = null;
/**
* The number of indexed characters if the column is only partly indexed,
* NULL if the entire column is indexed.
*
* @var integer
*/
private $_sub_part = null;
/**
* Contains YES if the column may contain NULL.
* If not, the column contains NO.
*
* @var string
*/
private $_null = '';
/**
* An estimate of the number of unique values in the index. This is updated
* by running ANALYZE TABLE or myisamchk -a. Cardinality is counted based on
* statistics stored as integers, so the value is not necessarily exact even
* for small tables. The higher the cardinality, the greater the chance that
* MySQL uses the index when doing joins.
*
* @var integer
*/
private $_cardinality = null;
/**
* Constructor
*
* @param array $params an array containing the parameters of the index column
*/
public function __construct($params = array())
{
$this->set($params);
}
/**
* Sets parameters of the index column
*
* @param array $params an array containing the parameters of the index column
*
* @return void
*/
public function set($params)
{
if (isset($params['Column_name'])) {
$this->_name = $params['Column_name'];
}
if (isset($params['Seq_in_index'])) {
$this->_seq_in_index = $params['Seq_in_index'];
}
if (isset($params['Collation'])) {
$this->_collation = $params['Collation'];
}
if (isset($params['Cardinality'])) {
$this->_cardinality = $params['Cardinality'];
}
if (isset($params['Sub_part'])) {
$this->_sub_part = $params['Sub_part'];
}
if (isset($params['Null'])) {
$this->_null = $params['Null'];
}
}
/**
* Returns the column name
*
* @return string column name
*/
public function getName()
{
return $this->_name;
}
/**
* Return the column collation
*
* @return string column collation
*/
public function getCollation()
{
return $this->_collation;
}
/**
* Returns the cardinality of the column
*
* @return int cardinality of the column
*/
public function getCardinality()
{
return $this->_cardinality;
}
/**
* Returns whether the column is nullable
*
* @param boolean $as_text whether to returned the string representation
*
* @return mixed nullability of the column. True/false or Yes/No depending
* on the value of the $as_text parameter
*/
public function getNull($as_text = false)
{
if ($as_text) {
if (!$this->_null || $this->_null == 'NO') {
return __('No');
} else {
return __('Yes');
}
} else {
return $this->_null;
}
}
/**
* Returns the sequence number of the column in the index
*
* @return int sequence number of the column in the index
*/
public function getSeqInIndex()
{
return $this->_seq_in_index;
}
/**
* Returns the number of indexed characters if the column is only
* partly indexed
*
* @return int the number of indexed characters
*/
public function getSubPart()
{
return $this->_sub_part;
}
/**
* Gets the properties in an array for comparison purposes
*
* @return array an array containing the properties of the index column
*/
public function getCompareData()
{
return array(
'Column_name' => $this->_name,
'Seq_in_index' => $this->_seq_in_index,
'Collation' => $this->_collation,
'Sub_part' => $this->_sub_part,
'Null' => $this->_null,
);
}
}

200
#pma/libraries/Language.php Normal file
View File

@ -0,0 +1,200 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold the PMA\libraries\LanguageManager class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use PMA\libraries\LanguageManager;
/**
* Language object
*
* @package PhpMyAdmin
*/
class Language
{
protected $code;
protected $name;
protected $native;
protected $regex;
protected $mysql;
/**
* Constructs the Language object
*
* @param string $code Language code
* @param string $name English name
* @param string $native Native name
* @param string $regex Match regullar expression
* @param string $mysql MySQL locale code
*
*/
public function __construct($code, $name, $native, $regex, $mysql)
{
$this->code = $code;
$this->name = $name;
$this->native = $native;
if (strpos($regex, '[-_]') === false) {
$regex = str_replace('|', '([-_][[:alpha:]]{2,3})?|', $regex);
}
$this->regex = $regex;
$this->mysql = $mysql;
}
/**
* Returns native name for language
*
* @return string
*/
public function getNativeName()
{
return $this->native;
}
/**
* Returns English name for language
*
* @return string
*/
public function getEnglishName()
{
return $this->name;
}
/**
* Returns verbose name for language
*
* @return string
*/
public function getName()
{
if (! empty($this->native)) {
return $this->native . ' - ' . $this->name;
} else {
return $this->name;
}
}
/**
* Returns language code
*
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* Returns MySQL locale code, can be empty
*
* @return string
*/
public function getMySQLLocale()
{
return $this->mysql;
}
/**
* Compare function used for sorting
*
* @param Language $other Other object to compare
*
* @return int same as strcmp
*/
public function cmp($other)
{
return strcmp($this->name, $other->name);
}
/**
* Checks whether language is currently active.
*
* @return bool
*/
public function isActive()
{
return $GLOBALS['lang'] == $this->code;
}
/**
* Checks whether language matches HTTP header Accept-Language.
*
* @param string $header Header content
*
* @return bool
*/
public function matchesAcceptLanguage($header)
{
$pattern = '/^('
. addcslashes($this->regex, '/')
. ')(;q=[0-9]\\.[0-9])?$/i';
return preg_match($pattern, $header);
}
/**
* Checks whether language matches HTTP header User-Agent
*
* @param string $header Header content
*
* @return bool
*/
public function matchesUserAgent($header)
{
$pattern = '/(\(|\[|;[[:space:]])('
. addcslashes($this->regex, '/')
. ')(;|\]|\))/i';
return preg_match($pattern, $header);
}
/**
* Checks whether langauge is RTL
*
* @return bool
*/
public function isRTL()
{
return in_array($this->code, array('ar', 'fa', 'he', 'ur'));
}
/**
* Activates given translation
*
* @return bool
*/
public function activate()
{
$GLOBALS['lang'] = $this->code;
// Set locale
_setlocale(LC_MESSAGES, $this->code);
_bindtextdomain('phpmyadmin', LOCALE_PATH);
_bind_textdomain_codeset('phpmyadmin', 'UTF-8');
_textdomain('phpmyadmin');
/* Text direction for language */
if ($this->isRTL()) {
$GLOBALS['text_dir'] = 'rtl';
} else {
$GLOBALS['text_dir'] = 'ltr';
}
/* TCPDF */
$GLOBALS['l'] = array();
/* TCPDF settings */
$GLOBALS['l']['a_meta_charset'] = 'UTF-8';
$GLOBALS['l']['a_meta_dir'] = $GLOBALS['text_dir'];
$GLOBALS['l']['a_meta_language'] = $this->code;
/* TCPDF translations */
$GLOBALS['l']['w_page'] = __('Page number:');
/* Show possible warnings from langauge selection */
LanguageManager::getInstance()->showWarnings();
}
}

View File

@ -0,0 +1,871 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold the PMA\libraries\LanguageManager class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use PMA\libraries\Language;
/**
* Language selection manager
*
* @package PhpMyAdmin
*/
class LanguageManager
{
/**
* @var array Definition data for languages
*
* Each member contains:
* - Language code
* - English language name
* - Native language name
* - Match regullar expression
* - MySQL locale
*/
private static $_language_data = array(
'af' => array(
'af',
'Afrikaans',
'',
'af|afrikaans',
'',
),
'ar' => array(
'ar',
'Arabic',
'&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;',
'ar|arabic',
'ar_AE',
),
'az' => array(
'az',
'Azerbaijani',
'Az&#601;rbaycanca',
'az|azerbaijani',
'',
),
'bn' => array(
'bn',
'Bangla',
'বাংলা',
'bn|bangla',
'',
),
'be' => array(
'be',
'Belarusian',
'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103;',
'be|belarusian',
'be_BY',
),
'be@latin' => array(
'be@latin',
'Belarusian (latin)',
'Bie&#0322;aruskaja',
'be[-_]lat|be@latin|belarusian latin',
'',
),
'bg' => array(
'bg',
'Bulgarian',
'&#1041;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;',
'bg|bulgarian',
'bg_BG',
),
'bs' => array(
'bs',
'Bosnian',
'Bosanski',
'bs|bosnian',
'',
),
'br' => array(
'br',
'Breton',
'Brezhoneg',
'br|breton',
'',
),
'brx' => array(
'brx',
'Bodo',
'बड़ो',
'brx|bodo',
'',
),
'ca' => array(
'ca',
'Catalan',
'Catal&agrave;',
'ca|catalan',
'ca_ES',
),
'ckb' => array(
'ckb',
'Sorani',
'سۆرانی',
'ckb|sorani',
'',
),
'cs' => array(
'cs',
'Czech',
'Čeština',
'cs|czech',
'cs_CZ',
),
'cy' => array(
'cy',
'Welsh',
'Cymraeg',
'cy|welsh',
'',
),
'da' => array(
'da',
'Danish',
'Dansk',
'da|danish',
'da_DK',
),
'de' => array(
'de',
'German',
'Deutsch',
'de|german',
'de_DE',
),
'el' => array(
'el',
'Greek',
'&Epsilon;&lambda;&lambda;&eta;&nu;&iota;&kappa;&#940;',
'el|greek',
'',
),
'en' => array(
'en',
'English',
'',
'en|english',
'en_US',
),
'en_gb' => array(
'en_GB',
'English (United Kingdom)',
'',
'en[_-]gb|english (United Kingdom)',
'en_GB',
),
'eo' => array(
'eo',
'Esperanto',
'Esperanto',
'eo|esperanto',
'',
),
'es' => array(
'es',
'Spanish',
'Espa&ntilde;ol',
'es|spanish',
'es_ES',
),
'et' => array(
'et',
'Estonian',
'Eesti',
'et|estonian',
'et_EE',
),
'eu' => array(
'eu',
'Basque',
'Euskara',
'eu|basque',
'eu_ES',
),
'fa' => array(
'fa',
'Persian',
'&#1601;&#1575;&#1585;&#1587;&#1740;',
'fa|persian',
'',
),
'fi' => array(
'fi',
'Finnish',
'Suomi',
'fi|finnish',
'fi_FI',
),
'fr' => array(
'fr',
'French',
'Fran&ccedil;ais',
'fr|french',
'fr_FR',
),
'fy' => array(
'fy',
'Frisian',
'Frysk',
'fy|frisian',
'',
),
'gl' => array(
'gl',
'Galician',
'Galego',
'gl|galician',
'gl_ES',
),
'gu' => array(
'gu',
'Gujarati',
'ગુજરાતી',
'gu|gujarati',
'gu_IN',
),
'he' => array(
'he',
'Hebrew',
'&#1506;&#1489;&#1512;&#1497;&#1514;',
'he|hebrew',
'he_IL',
),
'hi' => array(
'hi',
'Hindi',
'&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;',
'hi|hindi',
'hi_IN',
),
'hr' => array(
'hr',
'Croatian',
'Hrvatski',
'hr|croatian',
'hr_HR',
),
'hu' => array(
'hu',
'Hungarian',
'Magyar',
'hu|hungarian',
'hu_HU',
),
'hy' => array(
'hy',
'Armenian',
'Հայերէն',
'hy|armenian',
'',
),
'ia' => array(
'ia',
'Interlingua',
'',
'ia|interlingua',
'',
),
'id' => array(
'id',
'Indonesian',
'Bahasa Indonesia',
'id|indonesian',
'id_ID',
),
'it' => array(
'it',
'Italian',
'Italiano',
'it|italian',
'it_IT',
),
'ja' => array(
'ja',
'Japanese',
'&#26085;&#26412;&#35486;',
'ja|japanese',
'ja_JP',
),
'ko' => array(
'ko',
'Korean',
'&#54620;&#44397;&#50612;',
'ko|korean',
'ko_KR',
),
'ka' => array(
'ka',
'Georgian',
'&#4325;&#4304;&#4320;&#4311;&#4323;&#4314;&#4312;',
'ka|georgian',
'',
),
'kk' => array(
'kk',
'Kazakh',
'Қазақ',
'kk|kazakh',
'',
),
'km' => array(
'km',
'Khmer',
'ខ្មែរ',
'km|khmer',
'',
),
'kn' => array(
'kn',
'Kannada',
'ಕನ್ನಡ',
'kn|kannada',
'',
),
'ksh' => array(
'ksh',
'Colognian',
'Kölsch',
'ksh|colognian',
'',
),
'ky' => array(
'ky',
'Kyrgyz',
'Кыргызча',
'ky|kyrgyz',
'',
),
'li' => array(
'li',
'Limburgish',
'Lèmbörgs',
'li|limburgish',
'',
),
'lt' => array(
'lt',
'Lithuanian',
'Lietuvi&#371;',
'lt|lithuanian',
'lt_LT',
),
'lv' => array(
'lv',
'Latvian',
'Latvie&scaron;u',
'lv|latvian',
'lv_LV',
),
'mk' => array(
'mk',
'Macedonian',
'Macedonian',
'mk|macedonian',
'mk_MK',
),
'ml' => array(
'ml',
'Malayalam',
'Malayalam',
'ml|malayalam',
'',
),
'mn' => array(
'mn',
'Mongolian',
'&#1052;&#1086;&#1085;&#1075;&#1086;&#1083;',
'mn|mongolian',
'mn_MN',
),
'ms' => array(
'ms',
'Malay',
'Bahasa Melayu',
'ms|malay',
'ms_MY',
),
'ne' => array(
'ne',
'Nepali',
'नेपाली',
'ne|nepali',
'',
),
'nb' => array(
'nb',
'Norwegian',
'Norsk',
'nb|norwegian',
'nb_NO',
),
'nl' => array(
'nl',
'Dutch',
'Nederlands',
'nl|dutch',
'nl_NL',
),
'pa' => array(
'pa',
'Punjabi',
'ਪੰਜਾਬੀ',
'pa|punjabi',
'',
),
'pl' => array(
'pl',
'Polish',
'Polski',
'pl|polish',
'pl_PL',
),
'pt_br' => array(
'pt_BR',
'Brazilian Portuguese',
'Portugu&ecirc;s',
'pt[-_]br|brazilian portuguese',
'pt_BR',
),
'pt' => array(
'pt',
'Portuguese',
'Portugu&ecirc;s',
'pt|portuguese',
'pt_PT',
),
'ro' => array(
'ro',
'Romanian',
'Rom&acirc;n&#259;',
'ro|romanian',
'ro_RO',
),
'ru' => array(
'ru',
'Russian',
'&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;',
'ru|russian',
'ru_RU',
),
'si' => array(
'si',
'Sinhala',
'&#3523;&#3538;&#3458;&#3524;&#3517;',
'si|sinhala',
'',
),
'sk' => array(
'sk',
'Slovak',
'Sloven&#269;ina',
'sk|slovak',
'sk_SK',
),
'sl' => array(
'sl',
'Slovenian',
'Sloven&scaron;&#269;ina',
'sl|slovenian',
'sl_SI',
),
'sq' => array(
'sq',
'Slbanian',
'Shqip',
'sq|albanian',
'sq_AL',
),
'sr@latin' => array(
'sr@latin',
'Serbian (latin)',
'Srpski',
'sr[-_]lat|sr@latin|serbian latin',
'sr_YU',
),
'sr' => array(
'sr',
'Serbian',
'&#1057;&#1088;&#1087;&#1089;&#1082;&#1080;',
'sr|serbian',
'sr_YU',
),
'sv' => array(
'sv',
'Swedish',
'Svenska',
'sv|swedish',
'sv_SE',
),
'ta' => array(
'ta',
'Tamil',
'தமிழ்',
'ta|tamil',
'ta_IN',
),
'te' => array(
'te',
'Telugu',
'తెలుగు',
'te|telugu',
'te_IN',
),
'th' => array(
'th',
'Thai',
'&#3616;&#3634;&#3625;&#3634;&#3652;&#3607;&#3618;',
'th|thai',
'th_TH',
),
'tk' => array(
'tk',
'Turkmen',
'Türkmençe',
'tk|turkmen',
'',
),
'tr' => array(
'tr',
'Turkish',
'T&uuml;rk&ccedil;e',
'tr|turkish',
'tr_TR',
),
'tt' => array(
'tt',
'Tatarish',
'Tatar&ccedil;a',
'tt|tatarish',
'',
),
'ug' => array(
'ug',
'Uyghur',
'ئۇيغۇرچە',
'ug|uyghur',
'',
),
'uk' => array(
'uk',
'Ukrainian',
'&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;',
'uk|ukrainian',
'uk_UA',
),
'ur' => array(
'ur',
'Urdu',
'اُردوُ',
'ur|urdu',
'ur_PK',
),
'uz@latin' => array(
'uz@latin',
'Uzbek (latin)',
'O&lsquo;zbekcha',
'uz[-_]lat|uz@latin|uzbek-latin',
'',
),
'uz' => array(
'uz',
'Uzbek (cyrillic)',
'&#1038;&#1079;&#1073;&#1077;&#1082;&#1095;&#1072;',
'uz[-_]cyr|uz@cyrillic|uzbek-cyrillic',
'',
),
'vi' => array(
'vi',
'Vietnamese',
'Tiếng Việt',
'vi|vietnamese',
'vi_VN',
),
'vls' => array(
'vls',
'Flemish',
'West-Vlams',
'vls|flemish',
'',
),
'zh_tw' => array(
'zh_TW',
'Chinese traditional',
'&#20013;&#25991;',
'zh[-_](tw|hk)|chinese traditional',
'zh_TW',
),
// only TW and HK use traditional Chinese while others (CN, SG, MY)
// use simplified Chinese
'zh_cn' => array(
'zh_CN',
'Chinese simplified',
'&#20013;&#25991;',
'zh(?![-_](tw|hk))([-_][[:alpha:]]{2,3})?|chinese simplified',
'zh_CN',
),
);
private $_available_locales;
private $_available_languages;
private $_lang_failed_cfg;
private $_lang_failed_cookie;
private $_lang_failed_request;
private static $instance;
/**
* Returns LanguageManager singleton
*
* @return LanguageManager
*/
public static function getInstance()
{
if (self::$instance === NULL) {
self::$instance = new LanguageManager;
}
return self::$instance;
}
/**
* Returns list of available locales
*
* @return array
*/
public function listLocaleDir()
{
$result = array('en');
/* Check for existing directory */
if (!is_dir(LOCALE_PATH)) {
return $result;
}
/* Open the directory */
$handle = @opendir(LOCALE_PATH);
/* This can happen if the kit is English-only */
if ($handle === false) {
return $result;
}
/* Process all files */
while (false !== ($file = readdir($handle))) {
$path = LOCALE_PATH
. '/' . $file
. '/LC_MESSAGES/phpmyadmin.mo';
if ($file != "."
&& $file != ".."
&& @file_exists($path)
) {
$result[] = $file;
}
}
/* Close the handle */
closedir($handle);
return $result;
}
/**
* Returns (cached) list of all available locales
*
* @return array of strings
*/
public function availableLocales()
{
if (! $this->_available_locales) {
if (empty($GLOBALS['cfg']['FilterLanguages'])) {
$this->_available_locales = $this->listLocaleDir();
} else {
$this->_available_locales = preg_grep(
'@' . $GLOBALS['cfg']['FilterLanguages'] . '@',
$this->listLocaleDir()
);
}
}
return $this->_available_locales;
}
/**
* Returns (cached) list of all available languages
*
* @return array of Language objects
*/
public function availableLanguages()
{
if (! $this->_available_languages) {
$this->_available_languages = array();
foreach($this->availableLocales() as $lang) {
$lang = strtolower($lang);
if (isset($this::$_language_data[$lang])) {
$data = $this::$_language_data[$lang];
$this->_available_languages[$lang] = new Language(
$data[0],
$data[1],
$data[2],
$data[3],
$data[4]
);
} else {
$this->_available_languages[$lang] = new Language(
$lang,
ucfirst($lang),
ucfirst($lang),
$lang,
''
);
}
}
}
return $this->_available_languages;
}
/**
* Returns (cached) list of all available languages sorted
* by name
*
* @return array of Language objects
*/
public function sortedLanguages()
{
$this->availableLanguages();
uasort($this->_available_languages, function($a, $b)
{
return $a->cmp($b);
}
);
return $this->_available_languages;
}
/**
* Return Language object for given code
*
* @param string $code Language code
*
* @return object|false Language object or false on failure
*/
public function getLanguage($code)
{
$code = strtolower($code);
$langs = $this->availableLanguages();
if (isset($langs[$code])) {
return $langs[$code];
}
return false;
}
/**
* Return currently active Language object
*
* @return object Language object
*/
public function getCurrentLanguage()
{
return $this->_available_languages[strtolower($GLOBALS['lang'])];
}
/**
* Activates language based on configuration, user preferences or
* browser
*
* @return Language
*/
public function selectLanguage()
{
// check forced language
if (! empty($GLOBALS['cfg']['Lang'])) {
$lang = $this->getLanguage($GLOBALS['cfg']['Lang']);
if ($lang !== false) {
return $lang;
}
$this->_lang_failed_cfg = true;
}
// Don't use REQUEST in following code as it might be confused by cookies
// with same name. Check user requested language (POST)
if (! empty($_POST['lang'])) {
$lang = $this->getLanguage($_POST['lang']);
if ($lang !== false) {
return $lang;
}
$this->_lang_failed_request = true;
}
// check user requested language (GET)
if (! empty($_GET['lang'])) {
$lang = $this->getLanguage($_GET['lang']);
if ($lang !== false) {
return $lang;
}
$this->_lang_failed_request = true;
}
// check previous set language
if (! empty($_COOKIE['pma_lang'])) {
$lang = $this->getLanguage($_COOKIE['pma_lang']);
if ($lang !== false) {
return $lang;
}
$this->_lang_failed_cookie = true;
}
$langs = $this->availableLanguages();
// try to find out user's language by checking its HTTP_ACCEPT_LANGUAGE variable;
$accepted_languages = PMA_getenv('HTTP_ACCEPT_LANGUAGE');
if ($accepted_languages) {
foreach (explode(',', $accepted_languages) as $header) {
foreach ($langs as $language) {
if ($language->matchesAcceptLanguage($header)) {
return $language;
}
}
}
}
// try to find out user's language by checking its HTTP_USER_AGENT variable
$user_agent = PMA_getenv('HTTP_USER_AGENT');
if (! empty($user_agent)) {
foreach ($langs as $language) {
if ($language->matchesUserAgent($user_agent)) {
return $language;
}
}
}
// Didn't catch any valid lang : we use the default settings
if (isset($langs[$GLOBALS['cfg']['DefaultLang']])) {
return $langs[$GLOBALS['cfg']['DefaultLang']];
}
// Fallback to English
return $langs['en'];
}
/**
* Displays warnings about invalid languages. This needs to be postponed
* to show messages at time when language is initialized.
*
* @return void
*/
public function showWarnings()
{
// now, that we have loaded the language strings we can send the errors
if ($this->_lang_failed_cfg
|| $this->_lang_failed_cookie
|| $this->_lang_failed_request
) {
trigger_error(
__('Ignoring unsupported language code.'),
E_USER_ERROR
);
}
}
}

180
#pma/libraries/Linter.php Normal file
View File

@ -0,0 +1,180 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Analyzes a query and gives user feedback.
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use SqlParser\Lexer;
use SqlParser\Parser;
use SqlParser\UtfString;
use SqlParser\Utils\Error as ParserError;
/**
* The linter itself.
*
* @package PhpMyAdmin
*/
class Linter
{
/**
* Gets the starting position of each line.
*
* @param string $str String to be analyzed.
*
* @return array
*/
public static function getLines($str)
{
if ((!($str instanceof UtfString))
&& (defined('USE_UTF_STRINGS')) && (USE_UTF_STRINGS)
) {
// If the lexer uses UtfString for processing then the position will
// represent the position of the character and not the position of
// the byte.
$str = new UtfString($str);
}
// The reason for using the strlen is that the length
// required is the length in bytes, not characters.
//
// Given the following string: `????+`, where `?` represents a
// multi-byte character (lets assume that every `?` is a 2-byte
// character) and `+` is a newline, the first value of `$i` is `0`
// and the last one is `4` (because there are 5 characters). Bytes
// `$str[0]` and `$str[1]` are the first character, `$str[2]` and
// `$str[3]` are the second one and `$str[4]` is going to be the
// first byte of the third character. The fourth and the last one
// (which is actually a new line) aren't going to be processed at
// all.
$len = ($str instanceof UtfString) ?
$str->length() : strlen($str);
$lines = array(0);
for ($i = 0; $i < $len; ++$i) {
if ($str[$i] === "\n") {
$lines[] = $i + 1;
}
}
return $lines;
}
/**
* Computes the number of the line and column given an absolute position.
*
* @param array $lines The starting position of each line.
* @param int $pos The absolute position
*
* @return array
*/
public static function findLineNumberAndColumn($lines, $pos)
{
$line = 0;
foreach ($lines as $lineNo => $lineStart) {
if ($lineStart > $pos) {
break;
}
$line = $lineNo;
}
return array($line, $pos - $lines[$line]);
}
/**
* Runs the linting process.
*
* @param string $query The query to be checked.
*
* @return array
*/
public static function lint($query)
{
// Disabling lint for huge queries to save some resources.
if (mb_strlen($query) > 10000) {
return array(
array(
'message' => __(
'Linting is disabled for this query because it exceeds the '
. 'maximum length.'
),
'fromLine' => 0,
'fromColumn' => 0,
'toLine' => 0,
'toColumn' => 0,
'severity' => 'warning',
)
);
}
/**
* Lexer used for tokenizing the query.
*
* @var Lexer
*/
$lexer = new Lexer($query);
/**
* Parsed used for analysing the query.
*
* @var Parser
*/
$parser = new Parser($lexer->list);
/**
* Array containing all errors.
*
* @var array
*/
$errors = ParserError::get(array($lexer, $parser));
/**
* The response containing of all errors.
*
* @var array
*/
$response = array();
/**
* The starting position for each line.
*
* CodeMirror requires relative position to line, but the parser stores
* only the absolute position of the character in string.
*
* @var array
*/
$lines = static::getLines($query);
// Building the response.
foreach ($errors as $idx => $error) {
// Starting position of the string that caused the error.
list($fromLine, $fromColumn) = static::findLineNumberAndColumn(
$lines, $error[3]
);
// Ending position of the string that caused the error.
list($toLine, $toColumn) = static::findLineNumberAndColumn(
$lines, $error[3] + mb_strlen($error[2])
);
// Building the response.
$response[] = array(
'message' => sprintf(
__('%1$s (near <code>%2$s</code>)'),
$error[0], $error[2]
),
'fromLine' => $fromLine,
'fromColumn' => $fromColumn,
'toLine' => $toLine,
'toColumn' => $toColumn,
'severity' => 'error',
);
}
// Sending back the answer.
return $response;
}
}

View File

@ -0,0 +1,121 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* hold the ListAbstract base class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use ArrayObject;
/**
* Generic list class
*
* @todo add caching
* @abstract
* @package PhpMyAdmin
* @since phpMyAdmin 2.9.10
*/
abstract class ListAbstract extends ArrayObject
{
/**
* @var mixed empty item
*/
protected $item_empty = '';
/**
* ListAbstract constructor
*
* @param array $array The input parameter accepts an array or an
* Object.
* @param int $flags Flags to control the behaviour of the
* ArrayObject object.
* @param string $iterator_class Specify the class that will be used for
* iteration of the ArrayObject object.
* ArrayIterator is the default class used.
*/
public function __construct(
$array = array(), $flags = 0, $iterator_class = "ArrayIterator"
) {
parent::__construct($array, $flags, $iterator_class);
}
/**
* defines what is an empty item (0, '', false or null)
*
* @return mixed an empty item
*/
public function getEmpty()
{
return $this->item_empty;
}
/**
* checks if the given db names exists in the current list, if there is
* missing at least one item it returns false otherwise true
*
* @return boolean true if all items exists, otherwise false
*/
public function exists()
{
$this_elements = $this->getArrayCopy();
foreach (func_get_args() as $result) {
if (! in_array($result, $this_elements)) {
return false;
}
}
return true;
}
/**
* returns HTML <option>-tags to be used inside <select></select>
*
* @param mixed $selected the selected db or true for
* selecting current db
* @param boolean $include_information_schema whether include information schema
*
* @return string HTML option tags
*/
public function getHtmlOptions(
$selected = '', $include_information_schema = true
) {
if (true === $selected) {
$selected = $this->getDefault();
}
$options = '';
foreach ($this as $each_item) {
if (false === $include_information_schema
&& $GLOBALS['dbi']->isSystemSchema($each_item)
) {
continue;
}
$options .= '<option value="' . htmlspecialchars($each_item) . '"';
if ($selected === $each_item) {
$options .= ' selected="selected"';
}
$options .= '>' . htmlspecialchars($each_item) . '</option>' . "\n";
}
return $options;
}
/**
* returns default item
*
* @return string default item
*/
public function getDefault()
{
return $this->getEmpty();
}
/**
* builds up the list
*
* @return void
*/
abstract public function build();
}

View File

@ -0,0 +1,185 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* holds the ListDatabase class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
require_once './libraries/check_user_privileges.lib.php';
/**
* handles database lists
*
* <code>
* $ListDatabase = new ListDatabase($userlink);
* </code>
*
* @todo this object should be attached to the PMA_Server object
*
* @package PhpMyAdmin
* @since phpMyAdmin 2.9.10
*/
class ListDatabase extends ListAbstract
{
/**
* @var mixed database link resource|object to be used
* @access protected
*/
protected $db_link = null;
/**
* @var mixed user database link resource|object
* @access protected
*/
protected $db_link_user = null;
/**
* Constructor
*
* @param mixed $db_link_user user database link resource|object
*/
public function __construct($db_link_user = null)
{
$this->db_link = $db_link_user;
$this->db_link_user = $db_link_user;
parent::__construct();
$this->build();
}
/**
* checks if the configuration wants to hide some databases
*
* @return void
*/
protected function checkHideDatabase()
{
if (empty($GLOBALS['cfg']['Server']['hide_db'])) {
return;
}
foreach ($this->getArrayCopy() as $key => $db) {
if (preg_match('/' . $GLOBALS['cfg']['Server']['hide_db'] . '/', $db)) {
$this->offsetUnset($key);
}
}
}
/**
* retrieves database list from server
*
* @param string $like_db_name usually a db_name containing wildcards
*
* @return array
*/
protected function retrieve($like_db_name = null)
{
$database_list = array();
$command = "";
if (! $GLOBALS['cfg']['Server']['DisableIS']) {
$command .= "SELECT `SCHEMA_NAME` FROM `INFORMATION_SCHEMA`.`SCHEMATA`";
if (null !== $like_db_name) {
$command .= " WHERE `SCHEMA_NAME` LIKE '" . $like_db_name . "'";
}
} else {
if ($GLOBALS['dbs_to_test'] === false || null !== $like_db_name) {
$command .= "SHOW DATABASES";
if (null !== $like_db_name) {
$command .= " LIKE '" . $like_db_name . "'";
}
} else {
foreach ($GLOBALS['dbs_to_test'] as $db) {
$database_list = array_merge(
$database_list, $this->retrieve($db)
);
}
}
}
if ($command) {
$database_list = $GLOBALS['dbi']->fetchResult(
$command, null, null, $this->db_link
);
}
if ($GLOBALS['cfg']['NaturalOrder']) {
natsort($database_list);
} else {
// need to sort anyway, otherwise information_schema
// goes at the top
sort($database_list);
}
return $database_list;
}
/**
* builds up the list
*
* @return void
*/
public function build()
{
if (! $this->checkOnlyDatabase()) {
$items = $this->retrieve();
$this->exchangeArray($items);
}
$this->checkHideDatabase();
}
/**
* checks the only_db configuration
*
* @return boolean false if there is no only_db, otherwise true
*/
protected function checkOnlyDatabase()
{
if (is_string($GLOBALS['cfg']['Server']['only_db'])
&& mb_strlen($GLOBALS['cfg']['Server']['only_db'])
) {
$GLOBALS['cfg']['Server']['only_db'] = array(
$GLOBALS['cfg']['Server']['only_db']
);
}
if (! is_array($GLOBALS['cfg']['Server']['only_db'])) {
return false;
}
$items = array();
foreach ($GLOBALS['cfg']['Server']['only_db'] as $each_only_db) {
// check if the db name contains wildcard,
// thus containing not escaped _ or %
if (! preg_match('/(^|[^\\\\])(_|%)/', $each_only_db)) {
// ... not contains wildcard
$items[] = Util::unescapeMysqlWildcards($each_only_db);
continue;
}
$items = array_merge($items, $this->retrieve($each_only_db));
}
$this->exchangeArray($items);
return true;
}
/**
* returns default item
*
* @return string default item
*/
public function getDefault()
{
if (mb_strlen($GLOBALS['db'])) {
return $GLOBALS['db'];
}
return $this->getEmpty();
}
}

635
#pma/libraries/Menu.php Normal file
View File

@ -0,0 +1,635 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Generates and renders the top menu
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Class for generating the top menu
*
* @package PhpMyAdmin
*/
class Menu
{
/**
* Server id
*
* @access private
* @var int
*/
private $_server;
/**
* Database name
*
* @access private
* @var string
*/
private $_db;
/**
* Table name
*
* @access private
* @var string
*/
private $_table;
/**
* Creates a new instance of Menu
*
* @param int $server Server id
* @param string $db Database name
* @param string $table Table name
*/
public function __construct($server, $db, $table)
{
$this->_server = $server;
$this->_db = $db;
$this->_table = $table;
}
/**
* Prints the menu and the breadcrumbs
*
* @return void
*/
public function display()
{
echo $this->getDisplay();
}
/**
* Returns the menu and the breadcrumbs as a string
*
* @return string
*/
public function getDisplay()
{
$retval = $this->_getBreadcrumbs();
$retval .= $this->_getMenu();
return $retval;
}
/**
* Returns hash for the menu and the breadcrumbs
*
* @return string
*/
public function getHash()
{
return substr(
md5($this->_getMenu() . $this->_getBreadcrumbs()),
0,
8
);
}
/**
* Returns the menu as HTML
*
* @return string HTML formatted menubar
*/
private function _getMenu()
{
$url_params = array('db' => $this->_db);
if (mb_strlen($this->_table)) {
$tabs = $this->_getTableTabs();
$url_params['table'] = $this->_table;
$level = 'table';
} else if (mb_strlen($this->_db)) {
$tabs = $this->_getDbTabs();
$level = 'db';
} else {
$tabs = $this->_getServerTabs();
$level = 'server';
}
$allowedTabs = $this->_getAllowedTabs($level);
foreach ($tabs as $key => $value) {
if (! array_key_exists($key, $allowedTabs)) {
unset($tabs[$key]);
}
}
return Util::getHtmlTabs($tabs, $url_params, 'topmenu', true);
}
/**
* Returns a list of allowed tabs for the current user for the given level
*
* @param string $level 'server', 'db' or 'table' level
*
* @return array list of allowed tabs
*/
private function _getAllowedTabs($level)
{
$allowedTabs = Util::getMenuTabList($level);
$cfgRelation = PMA_getRelationsParam();
if ($cfgRelation['menuswork']) {
$groupTable = Util::backquote($cfgRelation['db'])
. "."
. Util::backquote($cfgRelation['usergroups']);
$userTable = Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['users']);
$sql_query = "SELECT `tab` FROM " . $groupTable
. " WHERE `allowed` = 'N'"
. " AND `tab` LIKE '" . $level . "%'"
. " AND `usergroup` = (SELECT usergroup FROM "
. $userTable . " WHERE `username` = '"
. $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user']) . "')";
$result = PMA_queryAsControlUser($sql_query, false);
if ($result) {
while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
$tabName = mb_substr(
$row['tab'],
mb_strpos($row['tab'], '_') + 1
);
unset($allowedTabs[$tabName]);
}
}
}
return $allowedTabs;
}
/**
* Returns the breadcrumbs as HTML
*
* @return string HTML formatted breadcrumbs
*/
private function _getBreadcrumbs()
{
$retval = '';
$tbl_is_view = $GLOBALS['dbi']->getTable($this->_db, $this->_table)
->isView();
if (empty($GLOBALS['cfg']['Server']['host'])) {
$GLOBALS['cfg']['Server']['host'] = '';
}
$server_info = ! empty($GLOBALS['cfg']['Server']['verbose'])
? $GLOBALS['cfg']['Server']['verbose']
: $GLOBALS['cfg']['Server']['host'];
$server_info .= empty($GLOBALS['cfg']['Server']['port'])
? ''
: ':' . $GLOBALS['cfg']['Server']['port'];
$separator = "<span class='separator item'>&nbsp;»</span>";
$item = '<a href="%1$s%2$s" class="item">';
if (Util::showText('TabsMode')) {
$item .= '%4$s: ';
}
$item .= '%3$s</a>';
$retval .= "<div id='floating_menubar'></div>";
$retval .= "<div id='serverinfo'>";
if (Util::showIcons('TabsMode')) {
$retval .= Util::getImage(
's_host.png',
'',
array('class' => 'item')
);
}
$retval .= sprintf(
$item,
Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabServer'], 'server'
),
PMA_URL_getCommon(),
htmlspecialchars($server_info),
__('Server')
);
if (mb_strlen($this->_db)) {
$retval .= $separator;
if (Util::showIcons('TabsMode')) {
$retval .= Util::getImage(
's_db.png',
'',
array('class' => 'item')
);
}
$retval .= sprintf(
$item,
Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabDatabase'], 'database'
),
PMA_URL_getCommon(array('db' => $this->_db)),
htmlspecialchars($this->_db),
__('Database')
);
// if the table is being dropped, $_REQUEST['purge'] is set to '1'
// so do not display the table name in upper div
if (mb_strlen($this->_table)
&& ! (isset($_REQUEST['purge']) && $_REQUEST['purge'] == '1')
) {
include './libraries/tbl_info.inc.php';
$retval .= $separator;
if (Util::showIcons('TabsMode')) {
$icon = $tbl_is_view ? 'b_views.png' : 's_tbl.png';
$retval .= Util::getImage(
$icon,
'',
array('class' => 'item')
);
}
$retval .= sprintf(
$item,
Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabTable'], 'table'
),
PMA_URL_getCommon(
array(
'db' => $this->_db, 'table' => $this->_table
)
),
str_replace(' ', '&nbsp;', htmlspecialchars($this->_table)),
$tbl_is_view ? __('View') : __('Table')
);
/**
* Displays table comment
*/
if (! empty($show_comment)
&& ! isset($GLOBALS['avoid_show_comment'])
) {
if (mb_strstr($show_comment, '; InnoDB free')) {
$show_comment = preg_replace(
'@; InnoDB free:.*?$@',
'',
$show_comment
);
}
$retval .= '<span class="table_comment"';
$retval .= ' id="span_table_comment">&quot;';
$retval .= htmlspecialchars($show_comment);
$retval .= '&quot;</span>';
} // end if
} else {
// no table selected, display database comment if present
$cfgRelation = PMA_getRelationsParam();
// Get additional information about tables for tooltip is done
// in Util::getDbInfo() only once
if ($cfgRelation['commwork']) {
$comment = PMA_getDbComment($this->_db);
/**
* Displays table comment
*/
if (! empty($comment)) {
$retval .= '<span class="table_comment"'
. ' id="span_table_comment">&quot;'
. htmlspecialchars($comment)
. '&quot;</span>';
} // end if
}
}
}
$retval .= '<div class="clearfloat"></div>';
$retval .= '</div>';
return $retval;
}
/**
* Returns the table tabs as an array
*
* @return array Data for generating table tabs
*/
private function _getTableTabs()
{
$db_is_system_schema = $GLOBALS['dbi']->isSystemSchema($this->_db);
$tbl_is_view = $GLOBALS['dbi']->getTable($this->_db, $this->_table)
->isView();
$updatable_view = false;
if ($tbl_is_view) {
$updatable_view = $GLOBALS['dbi']->getTable($this->_db, $this->_table)
->isUpdatableView();
}
$is_superuser = $GLOBALS['dbi']->isSuperuser();
$isCreateOrGrantUser = $GLOBALS['dbi']->isUserType('grant')
|| $GLOBALS['dbi']->isUserType('create');
$tabs = array();
$tabs['browse']['icon'] = 'b_browse.png';
$tabs['browse']['text'] = __('Browse');
$tabs['browse']['link'] = 'sql.php';
$tabs['browse']['args']['pos'] = 0;
$tabs['structure']['icon'] = 'b_props.png';
$tabs['structure']['link'] = 'tbl_structure.php';
$tabs['structure']['text'] = __('Structure');
$tabs['structure']['active'] = in_array(
basename($GLOBALS['PMA_PHP_SELF']),
array('tbl_structure.php', 'tbl_relation.php')
);
$tabs['sql']['icon'] = 'b_sql.png';
$tabs['sql']['link'] = 'tbl_sql.php';
$tabs['sql']['text'] = __('SQL');
$tabs['search']['icon'] = 'b_search.png';
$tabs['search']['text'] = __('Search');
$tabs['search']['link'] = 'tbl_select.php';
$tabs['search']['active'] = in_array(
basename($GLOBALS['PMA_PHP_SELF']),
array('tbl_select.php', 'tbl_zoom_select.php', 'tbl_find_replace.php')
);
if (! $db_is_system_schema && (! $tbl_is_view || $updatable_view)) {
$tabs['insert']['icon'] = 'b_insrow.png';
$tabs['insert']['link'] = 'tbl_change.php';
$tabs['insert']['text'] = __('Insert');
}
$tabs['export']['icon'] = 'b_tblexport.png';
$tabs['export']['link'] = 'tbl_export.php';
$tabs['export']['args']['single_table'] = 'true';
$tabs['export']['text'] = __('Export');
/**
* Don't display "Import" for views and information_schema
*/
if (! $tbl_is_view && ! $db_is_system_schema) {
$tabs['import']['icon'] = 'b_tblimport.png';
$tabs['import']['link'] = 'tbl_import.php';
$tabs['import']['text'] = __('Import');
}
if (($is_superuser || $isCreateOrGrantUser)
&& ! $db_is_system_schema
) {
$tabs['privileges']['link'] = 'server_privileges.php';
$tabs['privileges']['args']['checkprivsdb'] = $this->_db;
$tabs['privileges']['args']['checkprivstable'] = $this->_table;
// stay on table view
$tabs['privileges']['args']['viewing_mode'] = 'table';
$tabs['privileges']['text'] = __('Privileges');
$tabs['privileges']['icon'] = 's_rights.png';
}
/**
* Don't display "Operations" for views and information_schema
*/
if (! $tbl_is_view && ! $db_is_system_schema) {
$tabs['operation']['icon'] = 'b_tblops.png';
$tabs['operation']['link'] = 'tbl_operations.php';
$tabs['operation']['text'] = __('Operations');
}
/**
* Views support a limited number of operations
*/
if ($tbl_is_view && ! $db_is_system_schema) {
$tabs['operation']['icon'] = 'b_tblops.png';
$tabs['operation']['link'] = 'view_operations.php';
$tabs['operation']['text'] = __('Operations');
}
if (Tracker::isActive() && ! $db_is_system_schema) {
$tabs['tracking']['icon'] = 'eye.png';
$tabs['tracking']['text'] = __('Tracking');
$tabs['tracking']['link'] = 'tbl_tracking.php';
}
if (! $db_is_system_schema
&& Util::currentUserHasPrivilege(
'TRIGGER',
$this->_db,
$this->_table
)
&& ! $tbl_is_view
) {
$tabs['triggers']['link'] = 'tbl_triggers.php';
$tabs['triggers']['text'] = __('Triggers');
$tabs['triggers']['icon'] = 'b_triggers.png';
}
return $tabs;
}
/**
* Returns the db tabs as an array
*
* @return array Data for generating db tabs
*/
private function _getDbTabs()
{
$db_is_system_schema = $GLOBALS['dbi']->isSystemSchema($this->_db);
$num_tables = count($GLOBALS['dbi']->getTables($this->_db));
$is_superuser = $GLOBALS['dbi']->isSuperuser();
$isCreateOrGrantUser = $GLOBALS['dbi']->isUserType('grant')
|| $GLOBALS['dbi']->isUserType('create');
/**
* Gets the relation settings
*/
$cfgRelation = PMA_getRelationsParam();
$tabs = array();
$tabs['structure']['link'] = 'db_structure.php';
$tabs['structure']['text'] = __('Structure');
$tabs['structure']['icon'] = 'b_props.png';
$tabs['sql']['link'] = 'db_sql.php';
$tabs['sql']['text'] = __('SQL');
$tabs['sql']['icon'] = 'b_sql.png';
$tabs['search']['text'] = __('Search');
$tabs['search']['icon'] = 'b_search.png';
$tabs['search']['link'] = 'db_search.php';
if ($num_tables == 0) {
$tabs['search']['warning'] = __('Database seems to be empty!');
}
$tabs['qbe']['text'] = __('Query');
$tabs['qbe']['icon'] = 's_db.png';
$tabs['qbe']['link'] = 'db_qbe.php';
if ($num_tables == 0) {
$tabs['qbe']['warning'] = __('Database seems to be empty!');
}
$tabs['export']['text'] = __('Export');
$tabs['export']['icon'] = 'b_export.png';
$tabs['export']['link'] = 'db_export.php';
if ($num_tables == 0) {
$tabs['export']['warning'] = __('Database seems to be empty!');
}
if (! $db_is_system_schema) {
$tabs['import']['link'] = 'db_import.php';
$tabs['import']['text'] = __('Import');
$tabs['import']['icon'] = 'b_import.png';
$tabs['operation']['link'] = 'db_operations.php';
$tabs['operation']['text'] = __('Operations');
$tabs['operation']['icon'] = 'b_tblops.png';
if (($is_superuser || $isCreateOrGrantUser)) {
$tabs['privileges']['link'] = 'server_privileges.php';
$tabs['privileges']['args']['checkprivsdb'] = $this->_db;
// stay on database view
$tabs['privileges']['args']['viewing_mode'] = 'db';
$tabs['privileges']['text'] = __('Privileges');
$tabs['privileges']['icon'] = 's_rights.png';
}
$tabs['routines']['link'] = 'db_routines.php';
$tabs['routines']['text'] = __('Routines');
$tabs['routines']['icon'] = 'b_routines.png';
if (Util::currentUserHasPrivilege('EVENT', $this->_db)) {
$tabs['events']['link'] = 'db_events.php';
$tabs['events']['text'] = __('Events');
$tabs['events']['icon'] = 'b_events.png';
}
if (Util::currentUserHasPrivilege('TRIGGER', $this->_db)) {
$tabs['triggers']['link'] = 'db_triggers.php';
$tabs['triggers']['text'] = __('Triggers');
$tabs['triggers']['icon'] = 'b_triggers.png';
}
}
if (Tracker::isActive() && ! $db_is_system_schema) {
$tabs['tracking']['text'] = __('Tracking');
$tabs['tracking']['icon'] = 'eye.png';
$tabs['tracking']['link'] = 'db_tracking.php';
}
if (! $db_is_system_schema) {
$tabs['designer']['text'] = __('Designer');
$tabs['designer']['icon'] = 'b_relations.png';
$tabs['designer']['link'] = 'db_designer.php';
$tabs['designer']['id'] = 'designer_tab';
}
if (! $db_is_system_schema
&& $cfgRelation['centralcolumnswork']
) {
$tabs['central_columns']['text'] = __('Central columns');
$tabs['central_columns']['icon'] = 'centralColumns.png';
$tabs['central_columns']['link'] = 'db_central_columns.php';
}
return $tabs;
}
/**
* Returns the server tabs as an array
*
* @return array Data for generating server tabs
*/
private function _getServerTabs()
{
$is_superuser = $GLOBALS['dbi']->isSuperuser();
$isCreateOrGrantUser = $GLOBALS['dbi']->isUserType('grant')
|| $GLOBALS['dbi']->isUserType('create');
if (Util::cacheExists('binary_logs')) {
$binary_logs = Util::cacheGet('binary_logs');
} else {
$binary_logs = $GLOBALS['dbi']->fetchResult(
'SHOW MASTER LOGS',
'Log_name',
null,
null,
DatabaseInterface::QUERY_STORE
);
Util::cacheSet('binary_logs', $binary_logs);
}
$tabs = array();
$tabs['databases']['icon'] = 's_db.png';
$tabs['databases']['link'] = 'server_databases.php';
$tabs['databases']['text'] = __('Databases');
$tabs['sql']['icon'] = 'b_sql.png';
$tabs['sql']['link'] = 'server_sql.php';
$tabs['sql']['text'] = __('SQL');
$tabs['status']['icon'] = 's_status.png';
$tabs['status']['link'] = 'server_status.php';
$tabs['status']['text'] = __('Status');
$tabs['status']['active'] = in_array(
basename($GLOBALS['PMA_PHP_SELF']),
array(
'server_status.php',
'server_status_advisor.php',
'server_status_monitor.php',
'server_status_queries.php',
'server_status_variables.php',
'server_status_processes.php'
)
);
if ($is_superuser || $isCreateOrGrantUser) {
$tabs['rights']['icon'] = 's_rights.png';
$tabs['rights']['link'] = 'server_privileges.php';
$tabs['rights']['text'] = __('User accounts');
$tabs['rights']['active'] = in_array(
basename($GLOBALS['PMA_PHP_SELF']),
array('server_privileges.php', 'server_user_groups.php')
);
$tabs['rights']['args']['viewing_mode'] = 'server';
}
$tabs['export']['icon'] = 'b_export.png';
$tabs['export']['link'] = 'server_export.php';
$tabs['export']['text'] = __('Export');
$tabs['import']['icon'] = 'b_import.png';
$tabs['import']['link'] = 'server_import.php';
$tabs['import']['text'] = __('Import');
$tabs['settings']['icon'] = 'b_tblops.png';
$tabs['settings']['link'] = 'prefs_manage.php';
$tabs['settings']['text'] = __('Settings');
$tabs['settings']['active'] = in_array(
basename($GLOBALS['PMA_PHP_SELF']),
array('prefs_forms.php', 'prefs_manage.php')
);
if (! empty($binary_logs)) {
$tabs['binlog']['icon'] = 's_tbl.png';
$tabs['binlog']['link'] = 'server_binlog.php';
$tabs['binlog']['text'] = __('Binary log');
}
if ($is_superuser) {
$tabs['replication']['icon'] = 's_replication.png';
$tabs['replication']['link'] = 'server_replication.php';
$tabs['replication']['text'] = __('Replication');
}
$tabs['vars']['icon'] = 's_vars.png';
$tabs['vars']['link'] = 'server_variables.php';
$tabs['vars']['text'] = __('Variables');
$tabs['charset']['icon'] = 's_asci.png';
$tabs['charset']['link'] = 'server_collations.php';
$tabs['charset']['text'] = __('Charsets');
$tabs['engine']['icon'] = 'b_engine.png';
$tabs['engine']['link'] = 'server_engines.php';
$tabs['engine']['text'] = __('Engines');
$tabs['plugins']['icon'] = 'b_plugin.png';
$tabs['plugins']['link'] = 'server_plugins.php';
$tabs['plugins']['text'] = __('Plugins');
return $tabs;
}
/**
* Set current table
*
* @param string $table Current table
*
* @return $this
*/
public function setTable($table)
{
$this->_table = $table;
return $this;
}
}

782
#pma/libraries/Message.php Normal file
View File

@ -0,0 +1,782 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds class Message
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* a single message
*
* simple usage examples:
* <code>
* // display simple error message 'Error'
* Message::error()->display();
*
* // get simple success message 'Success'
* $message = Message::success();
*
* // get special notice
* $message = Message::notice(__('This is a localized notice'));
* </code>
*
* more advanced usage example:
* <code>
* // create a localized success message
* $message = Message::success('strSomeLocaleMessage');
*
* // create another message, a hint, with a localized string which expects
* // two parameters: $strSomeTooltip = 'Read the %smanual%s'
* $hint = Message::notice('strSomeTooltip');
* // replace placeholders with the following params
* $hint->addParam('[doc@cfg_Example]');
* $hint->addParam('[/doc]');
* // add this hint as a tooltip
* $hint = showHint($hint);
*
* // add the retrieved tooltip reference to the original message
* $message->addMessage($hint);
*
* // create another message ...
* $more = Message::notice('strSomeMoreLocale');
* $more->addString('strSomeEvenMoreLocale', '<br />');
* $more->addParam('parameter for strSomeMoreLocale');
* $more->addParam('more parameter for strSomeMoreLocale');
*
* // and add it also to the original message
* $message->addMessage($more);
* // finally add another raw message
* $message->addMessage('some final words', ' - ');
*
* // display() will now print all messages in the same order as they are added
* $message->display();
* // strSomeLocaleMessage <sup>1</sup> strSomeMoreLocale<br />
* // strSomeEvenMoreLocale - some final words
* </code>
*
* @package PhpMyAdmin
*/
class Message
{
const SUCCESS = 1; // 0001
const NOTICE = 2; // 0010
const ERROR = 8; // 1000
const SANITIZE_NONE = 0; // 0000 0000
const SANITIZE_STRING = 16; // 0001 0000
const SANITIZE_PARAMS = 32; // 0010 0000
const SANITIZE_BOOTH = 48; // 0011 0000
/**
* message levels
*
* @var array
*/
static public $level = array (
Message::SUCCESS => 'success',
Message::NOTICE => 'notice',
Message::ERROR => 'error',
);
/**
* The message number
*
* @access protected
* @var integer
*/
protected $number = Message::NOTICE;
/**
* The locale string identifier
*
* @access protected
* @var string
*/
protected $string = '';
/**
* The formatted message
*
* @access protected
* @var string
*/
protected $message = '';
/**
* Whether the message was already displayed
*
* @access protected
* @var boolean
*/
protected $isDisplayed = false;
/**
* Whether to use BB code when displaying.
*
* @access protected
* @var boolean
*/
protected $useBBCode = true;
/**
* Unique id
*
* @access protected
* @var string
*/
protected $hash = null;
/**
* holds parameters
*
* @access protected
* @var array
*/
protected $params = array();
/**
* holds additional messages
*
* @access protected
* @var array
*/
protected $addedMessages = array();
/**
* Constructor
*
* @param string $string The message to be displayed
* @param integer $number A numeric representation of the type of message
* @param array $params An array of parameters to use in the message
* @param integer $sanitize A flag to indicate what to sanitize, see
* constant definitions above
*/
public function __construct($string = '', $number = Message::NOTICE,
$params = array(), $sanitize = Message::SANITIZE_NONE
) {
$this->setString($string, $sanitize & Message::SANITIZE_STRING);
$this->setNumber($number);
$this->setParams($params, $sanitize & Message::SANITIZE_PARAMS);
}
/**
* magic method: return string representation for this object
*
* @return string
*/
public function __toString()
{
return $this->getMessage();
}
/**
* get Message of type success
*
* shorthand for getting a simple success message
*
* @param string $string A localized string
* e.g. __('Your SQL query has been
* executed successfully')
*
* @return Message
* @static
*/
static public function success($string = '')
{
if (empty($string)) {
$string = __('Your SQL query has been executed successfully.');
}
return new Message($string, Message::SUCCESS);
}
/**
* get Message of type error
*
* shorthand for getting a simple error message
*
* @param string $string A localized string e.g. __('Error')
*
* @return Message
* @static
*/
static public function error($string = '')
{
if (empty($string)) {
$string = __('Error');
}
return new Message($string, Message::ERROR);
}
/**
* get Message of type notice
*
* shorthand for getting a simple notice message
*
* @param string $string A localized string
* e.g. __('The additional features for working with
* linked tables have been deactivated. To find out
* why click %shere%s.')
*
* @return Message
* @static
*/
static public function notice($string)
{
return new Message($string, Message::NOTICE);
}
/**
* get Message with customized content
*
* shorthand for getting a customized message
*
* @param string $message A localized string
* @param integer $type A numeric representation of the type of message
*
* @return Message
* @static
*/
static public function raw($message, $type = Message::NOTICE)
{
$r = new Message('', $type);
$r->setMessage($message);
$r->setBBCode(false);
return $r;
}
/**
* get Message for number of affected rows
*
* shorthand for getting a customized message
*
* @param integer $rows Number of rows
*
* @return Message
* @static
*/
static public function getMessageForAffectedRows($rows)
{
$message = Message::success(
_ngettext('%1$d row affected.', '%1$d rows affected.', $rows)
);
$message->addParam($rows);
return $message;
}
/**
* get Message for number of deleted rows
*
* shorthand for getting a customized message
*
* @param integer $rows Number of rows
*
* @return Message
* @static
*/
static public function getMessageForDeletedRows($rows)
{
$message = Message::success(
_ngettext('%1$d row deleted.', '%1$d rows deleted.', $rows)
);
$message->addParam($rows);
return $message;
}
/**
* get Message for number of inserted rows
*
* shorthand for getting a customized message
*
* @param integer $rows Number of rows
*
* @return Message
* @static
*/
static public function getMessageForInsertedRows($rows)
{
$message = Message::success(
_ngettext('%1$d row inserted.', '%1$d rows inserted.', $rows)
);
$message->addParam($rows);
return $message;
}
/**
* get Message of type error with custom content
*
* shorthand for getting a customized error message
*
* @param string $message A localized string
*
* @return Message
* @static
*/
static public function rawError($message)
{
return Message::raw($message, Message::ERROR);
}
/**
* get Message of type notice with custom content
*
* shorthand for getting a customized notice message
*
* @param string $message A localized string
*
* @return Message
* @static
*/
static public function rawNotice($message)
{
return Message::raw($message, Message::NOTICE);
}
/**
* get Message of type success with custom content
*
* shorthand for getting a customized success message
*
* @param string $message A localized string
*
* @return Message
* @static
*/
static public function rawSuccess($message)
{
return Message::raw($message, Message::SUCCESS);
}
/**
* returns whether this message is a success message or not
* and optionally makes this message a success message
*
* @param boolean $set Whether to make this message of SUCCESS type
*
* @return boolean whether this is a success message or not
*/
public function isSuccess($set = false)
{
if ($set) {
$this->setNumber(Message::SUCCESS);
}
return $this->getNumber() === Message::SUCCESS;
}
/**
* returns whether this message is a notice message or not
* and optionally makes this message a notice message
*
* @param boolean $set Whether to make this message of NOTICE type
*
* @return boolean whether this is a notice message or not
*/
public function isNotice($set = false)
{
if ($set) {
$this->setNumber(Message::NOTICE);
}
return $this->getNumber() === Message::NOTICE;
}
/**
* returns whether this message is an error message or not
* and optionally makes this message an error message
*
* @param boolean $set Whether to make this message of ERROR type
*
* @return boolean Whether this is an error message or not
*/
public function isError($set = false)
{
if ($set) {
$this->setNumber(Message::ERROR);
}
return $this->getNumber() === Message::ERROR;
}
/**
* Set whether we should use BB Code when rendering.
*
* @param boolean $useBBCode Use BB Code?
*
* @return void
*/
public function setBBCode($useBBCode)
{
$this->useBBCode = $useBBCode;
}
/**
* set raw message (overrides string)
*
* @param string $message A localized string
* @param boolean $sanitize Whether to sanitize $message or not
*
* @return void
*/
public function setMessage($message, $sanitize = false)
{
if ($sanitize) {
$message = Message::sanitize($message);
}
$this->message = $message;
}
/**
* set string (does not take effect if raw message is set)
*
* @param string $string string to set
* @param boolean $sanitize whether to sanitize $string or not
*
* @return void
*/
public function setString($string, $sanitize = true)
{
if ($sanitize) {
$string = Message::sanitize($string);
}
$this->string = $string;
}
/**
* set message type number
*
* @param integer $number message type number to set
*
* @return void
*/
public function setNumber($number)
{
$this->number = $number;
}
/**
* add parameter, usually in conjunction with strings
*
* usage
* <code>
* $message->addParam('strLocale', false);
* $message->addParam('[em]some string[/em]');
* $message->addParam('<img src="img" />', false);
* </code>
*
* @param mixed $param parameter to add
* @param boolean $raw whether parameter should be passed as is
* without html escaping
*
* @return void
*/
public function addParam($param, $raw = true)
{
if ($param instanceof Message) {
$this->params[] = $param;
} elseif ($raw) {
$this->params[] = htmlspecialchars($param);
} else {
$this->params[] = Message::notice($param);
}
}
/**
* add another string to be concatenated on displaying
*
* @param string $string to be added
* @param string $separator to use between this and previous string/message
*
* @return void
*/
public function addString($string, $separator = ' ')
{
$this->addedMessages[] = $separator;
$this->addedMessages[] = Message::notice($string);
}
/**
* add a bunch of messages at once
*
* @param array $messages to be added
* @param string $separator to use between this and previous string/message
*
* @return void
*/
public function addMessages($messages, $separator = ' ')
{
foreach ($messages as $message) {
$this->addMessage($message, $separator);
}
}
/**
* add another raw message to be concatenated on displaying
*
* @param mixed $message to be added
* @param string $separator to use between this and previous string/message
*
* @return void
*/
public function addMessage($message, $separator = ' ')
{
if (mb_strlen($separator)) {
$this->addedMessages[] = $separator;
}
if ($message instanceof Message) {
$this->addedMessages[] = $message;
} else {
$this->addedMessages[] = Message::rawNotice($message);
}
}
/**
* set all params at once, usually used in conjunction with string
*
* @param array|string $params parameters to set
* @param boolean $sanitize whether to sanitize params
*
* @return void
*/
public function setParams($params, $sanitize = false)
{
if ($sanitize) {
$params = Message::sanitize($params);
}
$this->params = $params;
}
/**
* return all parameters
*
* @return array
*/
public function getParams()
{
return $this->params;
}
/**
* return all added messages
*
* @return array
*/
public function getAddedMessages()
{
return $this->addedMessages;
}
/**
* Sanitizes $message
*
* @param mixed $message the message(s)
*
* @return mixed the sanitized message(s)
* @access public
* @static
*/
static public function sanitize($message)
{
if (is_array($message)) {
foreach ($message as $key => $val) {
$message[$key] = Message::sanitize($val);
}
return $message;
}
return htmlspecialchars($message);
}
/**
* decode $message, taking into account our special codes
* for formatting
*
* @param string $message the message
*
* @return string the decoded message
* @access public
* @static
*/
static public function decodeBB($message)
{
return PMA_sanitize($message, false, true);
}
/**
* wrapper for sprintf()
*
* @return string formatted
*/
static public function format()
{
$params = func_get_args();
if (isset($params[1]) && is_array($params[1])) {
array_unshift($params[1], $params[0]);
$params = $params[1];
}
return call_user_func_array('sprintf', $params);
}
/**
* returns unique Message::$hash, if not exists it will be created
*
* @return string Message::$hash
*/
public function getHash()
{
if (null === $this->hash) {
$this->hash = md5(
$this->getNumber() .
$this->string .
$this->message
);
}
return $this->hash;
}
/**
* returns compiled message
*
* @return string complete message
*/
public function getMessage()
{
$message = $this->message;
if (0 === mb_strlen($message)) {
$string = $this->getString();
if (isset($GLOBALS[$string])) {
$message = $GLOBALS[$string];
} elseif (0 === mb_strlen($string)) {
$message = '';
} else {
$message = $string;
}
}
if ($this->isDisplayed()) {
$message = $this->getMessageWithIcon($message);
}
if (count($this->getParams()) > 0) {
$message = Message::format($message, $this->getParams());
}
if ($this->useBBCode) {
$message = Message::decodeBB($message);
}
foreach ($this->getAddedMessages() as $add_message) {
$message .= $add_message;
}
return $message;
}
/**
* Returns only message string without image & other HTML.
*
* @return string
*/
public function getOnlyMessage()
{
return $this->message;
}
/**
* returns Message::$string
*
* @return string Message::$string
*/
public function getString()
{
return $this->string;
}
/**
* returns Message::$number
*
* @return integer Message::$number
*/
public function getNumber()
{
return $this->number;
}
/**
* returns level of message
*
* @return string level of message
*/
public function getLevel()
{
return Message::$level[$this->getNumber()];
}
/**
* Displays the message in HTML
*
* @return void
*/
public function display()
{
echo $this->getDisplay();
$this->isDisplayed(true);
}
/**
* returns HTML code for displaying this message
*
* @return string whole message box
*/
public function getDisplay()
{
$this->isDisplayed(true);
return '<div class="' . $this->getLevel() . '">'
. $this->getMessage() . '</div>';
}
/**
* sets and returns whether the message was displayed or not
*
* @param boolean $isDisplayed whether to set displayed flag
*
* @return boolean Message::$isDisplayed
*/
public function isDisplayed($isDisplayed = false)
{
if ($isDisplayed) {
$this->isDisplayed = true;
}
return $this->isDisplayed;
}
/**
* Returns the message with corresponding image icon
*
* @param string $message the message(s)
*
* @return string message with icon
*/
public function getMessageWithIcon($message)
{
if ('error' == $this->getLevel()) {
$image = 's_error.png';
} elseif ('success' == $this->getLevel()) {
$image = 's_success.png';
} else {
$image = 's_notice.png';
}
$message = Message::notice(Util::getImage($image)) . " " . $message;
return $message;
}
}

View File

@ -0,0 +1,138 @@
<?php /* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Output buffering wrapper
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Output buffering wrapper class
*
* @package PhpMyAdmin
*/
class OutputBuffering
{
private static $_instance;
private $_mode;
private $_content;
private $_on;
/**
* Initializes class
*/
private function __construct()
{
$this->_mode = $this->_getMode();
$this->_on = false;
}
/**
* This function could be used eventually to support more modes.
*
* @return integer the output buffer mode
*/
private function _getMode()
{
$mode = 0;
if ($GLOBALS['cfg']['OBGzip'] && function_exists('ob_start')) {
if (ini_get('output_handler') == 'ob_gzhandler') {
// If a user sets the output_handler in php.ini to ob_gzhandler, then
// any right frame file in phpMyAdmin will not be handled properly by
// the browser. My fix was to check the ini file within the
// PMA_outBufferModeGet() function.
$mode = 0;
} elseif (function_exists('ob_get_level') && ob_get_level() > 0) {
// happens when php.ini's output_buffering is not Off
ob_end_clean();
$mode = 1;
} else {
$mode = 1;
}
}
// Zero (0) is no mode or in other words output buffering is OFF.
// Follow 2^0, 2^1, 2^2, 2^3 type values for the modes.
// Useful if we ever decide to combine modes. Then a bitmask field of
// the sum of all modes will be the natural choice.
return $mode;
}
/**
* Returns the singleton OutputBuffering object
*
* @return OutputBuffering object
*/
public static function getInstance()
{
if (empty(self::$_instance)) {
self::$_instance = new OutputBuffering();
}
return self::$_instance;
}
/**
* This function will need to run at the top of all pages if output
* output buffering is turned on. It also needs to be passed $mode from
* the PMA_outBufferModeGet() function or it will be useless.
*
* @return void
*/
public function start()
{
if (! $this->_on) {
if ($this->_mode && function_exists('ob_gzhandler')) {
ob_start('ob_gzhandler');
}
ob_start();
if (! defined('TESTSUITE')) {
header('X-ob_mode: ' . $this->_mode);
}
register_shutdown_function(
array('PMA\libraries\OutputBuffering', 'stop')
);
$this->_on = true;
}
}
/**
* This function will need to run at the bottom of all pages if output
* buffering is turned on. It also needs to be passed $mode from the
* PMA_outBufferModeGet() function or it will be useless.
*
* @return void
*/
public static function stop()
{
$buffer = OutputBuffering::getInstance();
if ($buffer->_on) {
$buffer->_on = false;
$buffer->_content = ob_get_contents();
ob_end_clean();
}
}
/**
* Gets buffer content
*
* @return string buffer content
*/
public function getContents()
{
return $this->_content;
}
/**
* Flushes output buffer
*
* @return void
*/
public function flush()
{
if (ob_get_status() && $this->_mode) {
ob_flush();
} else {
flush();
}
}
}

150
#pma/libraries/PDF.php Normal file
View File

@ -0,0 +1,150 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* TCPDF wrapper class.
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use TCPDF;
use TCPDF_FONTS;
require_once TCPDF_INC;
/**
* PDF export base class providing basic configuration.
*
* @package PhpMyAdmin
*/
class PDF extends TCPDF
{
var $footerset;
var $Alias = array();
/**
* PDF font to use.
*/
const PMA_PDF_FONT = 'DejaVuSans';
/**
* Constructs PDF and configures standard parameters.
*
* @param string $orientation page orientation
* @param string $unit unit
* @param string $format the format used for pages
* @param boolean $unicode true means that the input text is unicode
* @param string $encoding charset encoding; default is UTF-8.
* @param boolean $diskcache if true reduce the RAM memory usage by caching
* temporary data on filesystem (slower).
* @param boolean $pdfa If TRUE set the document to PDF/A mode.
*
* @access public
*/
public function __construct($orientation = 'P', $unit = 'mm', $format = 'A4',
$unicode = true, $encoding = 'UTF-8', $diskcache = false, $pdfa=false
) {
parent::__construct(
$orientation, $unit, $format, $unicode,
$encoding, $diskcache, $pdfa
);
$this->SetAuthor('phpMyAdmin ' . PMA_VERSION);
$this->AddFont('DejaVuSans', '', 'dejavusans.php');
$this->AddFont('DejaVuSans', 'B', 'dejavusansb.php');
$this->SetFont(PDF::PMA_PDF_FONT, '', 14);
$this->setFooterFont(array(PDF::PMA_PDF_FONT, '', 14));
}
/**
* This function must be named "Footer" to work with the TCPDF library
*
* @return void
*/
public function Footer()
{
// Check if footer for this page already exists
if (!isset($this->footerset[$this->page])) {
$this->SetY(-15);
$this->SetFont(PDF::PMA_PDF_FONT, '', 14);
$this->Cell(
0, 6,
__('Page number:') . ' '
. $this->getAliasNumPage() . '/' . $this->getAliasNbPages(),
'T', 0, 'C'
);
$this->Cell(0, 6, Util::localisedDate(), 0, 1, 'R');
$this->SetY(20);
// set footerset
$this->footerset[$this->page] = 1;
}
}
/**
* Function to set alias which will be expanded on page rendering.
*
* @param string $name name of the alias
* @param string $value value of the alias
*
* @return void
*/
public function SetAlias($name, $value)
{
$name = TCPDF_FONTS::UTF8ToUTF16BE(
$name, false, true, $this->CurrentFont
);
$this->Alias[$name] = TCPDF_FONTS::UTF8ToUTF16BE(
$value, false, true, $this->CurrentFont
);
}
/**
* Improved with alias expanding.
*
* @return void
*/
public function _putpages()
{
if (count($this->Alias) > 0) {
$nbPages = count($this->pages);
for ($n = 1; $n <= $nbPages; $n++) {
$this->pages[$n] = strtr($this->pages[$n], $this->Alias);
}
}
parent::_putpages();
}
/**
* Displays an error message
*
* @param string $error_message the error message
*
* @return void
*/
public function Error($error_message = '')
{
Message::error(
__('Error while creating PDF:') . ' ' . $error_message
)->display();
exit;
}
/**
* Sends file as a download to user.
*
* @param string $filename file name
*
* @return void
*/
public function Download($filename)
{
$pdfData = $this->getPDFData();
Response::getInstance()->disable();
PMA_downloadHeader(
$filename,
'application/pdf',
strlen($pdfData)
);
echo $pdfData;
}
}

View File

@ -0,0 +1,264 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Library for extracting information about the partitions
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* base Partition Class
*
* @package PhpMyAdmin
*/
class Partition extends SubPartition
{
/**
* @var string partition description
*/
protected $description;
/**
* @var SubPartition[] sub partitions
*/
protected $subPartitions = array();
/**
* Loads data from the fetched row from information_schema.PARTITIONS
*
* @param array $row fetched row
*
* @return void
*/
protected function loadData($row)
{
$this->name = $row['PARTITION_NAME'];
$this->ordinal = $row['PARTITION_ORDINAL_POSITION'];
$this->method = $row['PARTITION_METHOD'];
$this->expression = $row['PARTITION_EXPRESSION'];
$this->description = $row['PARTITION_DESCRIPTION'];
// no sub partitions, load all data to this object
if (empty($row['SUBPARTITION_NAME'])) {
$this->loadCommonData($row);
}
}
/**
* Returns the partiotion description
*
* @return string partition description
*/
public function getDescription()
{
return $this->description;
}
/**
* Add a sub partition
*
* @param SubPartition $partition Sub partition
*
* @return void
*/
public function addSubPartition(SubPartition $partition)
{
$this->subPartitions[] = $partition;
}
/**
* Whether there are sub partitions
*
* @return boolean
*/
public function hasSubPartitions()
{
return ! empty($this->subPartitions);
}
/**
* Returns the number of data rows
*
* @return integer number of rows
*/
public function getRows()
{
if (empty($this->subPartitions)) {
return $this->rows;
} else {
$rows = 0;
foreach ($this->subPartitions as $subPartition) {
$rows += $subPartition->rows;
}
return $rows;
}
}
/**
* Returns the total data length
*
* @return integer data length
*/
public function getDataLength()
{
if (empty($this->subPartitions)) {
return $this->dataLength;
} else {
$dataLength = 0;
foreach ($this->subPartitions as $subPartition) {
$dataLength += $subPartition->dataLength;
}
return $dataLength;
}
}
/**
* Returns the tatal index length
*
* @return integer index length
*/
public function getIndexLength()
{
if (empty($this->subPartitions)) {
return $this->indexLength;
} else {
$indexLength = 0;
foreach ($this->subPartitions as $subPartition) {
$indexLength += $subPartition->indexLength;
}
return $indexLength;
}
}
/**
* Returns the list of sub partitions
*
* @return SubPartition[]
*/
public function getSubPartitions()
{
return $this->subPartitions;
}
/**
* Returns array of partitions for a specific db/table
*
* @param string $db database name
* @param string $table table name
*
* @access public
* @return Partition[]
*/
static public function getPartitions($db, $table)
{
if (Partition::havePartitioning()) {
$result = $GLOBALS['dbi']->fetchResult(
"SELECT * FROM `information_schema`.`PARTITIONS`"
. " WHERE `TABLE_SCHEMA` = '" . $GLOBALS['dbi']->escapeString($db)
. "' AND `TABLE_NAME` = '" . $GLOBALS['dbi']->escapeString($table) . "'"
);
if ($result) {
$partitionMap = array();
foreach ($result as $row) {
if (isset($partitionMap[$row['PARTITION_NAME']])) {
$partition = $partitionMap[$row['PARTITION_NAME']];
} else {
$partition = new Partition($row);
$partitionMap[$row['PARTITION_NAME']] = $partition;
}
if (! empty($row['SUBPARTITION_NAME'])) {
$parentPartition = $partition;
$partition = new SubPartition($row);
$parentPartition->addSubPartition($partition);
}
}
return array_values($partitionMap);
}
return array();
} else {
return array();
}
}
/**
* returns array of partition names for a specific db/table
*
* @param string $db database name
* @param string $table table name
*
* @access public
* @return array of partition names
*/
static public function getPartitionNames($db, $table)
{
if (Partition::havePartitioning()) {
return $GLOBALS['dbi']->fetchResult(
"SELECT DISTINCT `PARTITION_NAME` FROM `information_schema`.`PARTITIONS`"
. " WHERE `TABLE_SCHEMA` = '" . $GLOBALS['dbi']->escapeString($db)
. "' AND `TABLE_NAME` = '" . $GLOBALS['dbi']->escapeString($table) . "'"
);
} else {
return array();
}
}
/**
* returns the partition method used by the table.
*
* @param string $db database name
* @param string $table table name
*
* @return string partition method
*/
static public function getPartitionMethod($db, $table)
{
if (Partition::havePartitioning()) {
$partition_method = $GLOBALS['dbi']->fetchResult(
"SELECT `PARTITION_METHOD` FROM `information_schema`.`PARTITIONS`"
. " WHERE `TABLE_SCHEMA` = '" . $GLOBALS['dbi']->escapeString($db) . "'"
. " AND `TABLE_NAME` = '" . $GLOBALS['dbi']->escapeString($table) . "'"
. " LIMIT 1"
);
if (! empty($partition_method)) {
return $partition_method[0];
}
}
return null;
}
/**
* checks if MySQL server supports partitioning
*
* @static
* @staticvar boolean $have_partitioning
* @staticvar boolean $already_checked
* @access public
* @return boolean
*/
static public function havePartitioning()
{
static $have_partitioning = false;
static $already_checked = false;
if (! $already_checked) {
if (PMA_MYSQL_INT_VERSION < 50600) {
if ($GLOBALS['dbi']->fetchValue(
"SELECT @@have_partitioning;"
)) {
$have_partitioning = true;
}
} else {
// see https://dev.mysql.com/doc/refman/5.6/en/partitioning.html
$plugins = $GLOBALS['dbi']->fetchResult("SHOW PLUGINS");
foreach ($plugins as $value) {
if ($value['Name'] == 'partition') {
$have_partitioning = true;
break;
}
}
}
$already_checked = true;
}
return $have_partitioning;
}
}

View File

@ -0,0 +1,171 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the Autoloader class
*
* @package PhpMyAdmin
*/
namespace PMA;
/**
* Holds the Autoloader class
*
* @package PhpMyAdmin
*/
class Psr4Autoloader
{
protected static $instance = null;
/**
* An associative array where the key is a namespace prefix and the value
* is an array of base directories for classes in that namespace.
*
* @var array
*/
protected $prefixes = array();
/**
* Get instance
*
* @return Psr4Autoloader
*/
public static function getInstance()
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Register loader with SPL autoloader stack.
*
* @return void
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
/**
* Adds a base directory for a namespace prefix.
*
* @param string $prefix The namespace prefix.
* @param string $base_dir A base directory for class files in the
* namespace.
* @param bool $prepend If true, prepend the base directory to the stack
* instead of appending it; this causes it to be
* searched first rather than last.
*
* @return void
*/
public function addNamespace($prefix, $base_dir, $prepend = false)
{
// normalize namespace prefix
$prefix = trim($prefix, '\\') . '\\';
// normalize the base directory with a trailing separator
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
// initialize the namespace prefix array
if (isset($this->prefixes[$prefix]) === false) {
$this->prefixes[$prefix] = array();
}
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift($this->prefixes[$prefix], $base_dir);
} else {
array_push($this->prefixes[$prefix], $base_dir);
}
}
/**
* Loads the class file for a given class name.
*
* @param string $class The fully-qualified class name.
*
* @return mixed The mapped file name on success, or boolean false on
* failure.
*/
public function loadClass($class)
{
// the current namespace prefix
$prefix = $class;
// work backwards through the namespace names of the fully-qualified
// class name to find a mapped file name
while (false !== $pos = strrpos($prefix, '\\')) {
// retain the trailing namespace separator in the prefix
$prefix = substr($class, 0, $pos + 1);
// the rest is the relative class name
$relative_class = substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
// remove the trailing namespace separator for the next iteration
// of strrpos()
$prefix = rtrim($prefix, '\\');
}
// never found a mapped file
return false;
}
/**
* Load the mapped file for a namespace prefix and relative class.
*
* @param string $prefix The namespace prefix.
* @param string $relative_class The relative class name.
*
* @return mixed Boolean false if no mapped file can be loaded, or the
* name of the mapped file that was loaded.
*/
protected function loadMappedFile($prefix, $relative_class)
{
// are there any base directories for this namespace prefix?
if (isset($this->prefixes[$prefix]) === false) {
return false;
}
// look through base directories for this namespace prefix
foreach ($this->prefixes[$prefix] as $base_dir) {
// replace the namespace prefix with the base directory,
// replace namespace separators with directory separators
// in the relative class name, append with .php
$file = $base_dir
. str_replace('\\', '/', $relative_class)
. '.php';
// if the mapped file exists, require it
if ($this->requireFile($file)) {
// yes, we're done
return $file;
}
}
// never found it
return false;
}
/**
* If a file exists, require it from the file system.
*
* @param string $file The file to require.
*
* @return bool True if the file exists, false if not.
*/
protected function requireFile($file)
{
if (file_exists($file)) {
include $file;
return true;
}
return false;
}
}

View File

@ -0,0 +1,390 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Recent and Favorite table list handling
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Handles the recently used and favorite tables.
*
* @TODO Change the release version in table pma_recent
* (#recent in documentation)
*
* @package PhpMyAdmin
*/
class RecentFavoriteTable
{
/**
* Reference to session variable containing recently used or favorite tables.
*
* @access private
* @var array
*/
private $_tables;
/**
* Defines type of action, Favorite or Recent table.
*
* @access private
* @var string
*/
private $_tableType;
/**
* RecentFavoriteTable instances.
*
* @access private
* @var array
*/
private static $_instances = array();
/**
* Creates a new instance of RecentFavoriteTable
*
* @param string $type the table type
*
* @access private
*/
private function __construct($type)
{
$this->_tableType = $type;
$server_id = $GLOBALS['server'];
if (! isset($_SESSION['tmpval'][$this->_tableType . '_tables'][$server_id])
) {
$_SESSION['tmpval'][$this->_tableType . '_tables'][$server_id]
= $this->_getPmaTable() ? $this->getFromDb() : array();
}
$this->_tables
=& $_SESSION['tmpval'][$this->_tableType . '_tables'][$server_id];
}
/**
* Returns class instance.
*
* @param string $type the table type
*
* @return RecentFavoriteTable
*/
public static function getInstance($type)
{
if (! array_key_exists($type, self::$_instances)) {
self::$_instances[$type] = new RecentFavoriteTable($type);
}
return self::$_instances[$type];
}
/**
* Returns the recent/favorite tables array
*
* @return array
*/
public function getTables()
{
return $this->_tables;
}
/**
* Returns recently used tables or favorite from phpMyAdmin database.
*
* @return array
*/
public function getFromDb()
{
// Read from phpMyAdmin database, if recent tables is not in session
$sql_query
= " SELECT `tables` FROM " . $this->_getPmaTable() .
" WHERE `username` = '" . $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user']) . "'";
$return = array();
$result = PMA_queryAsControlUser($sql_query, false);
if ($result) {
$row = $GLOBALS['dbi']->fetchArray($result);
if (isset($row[0])) {
$return = json_decode($row[0], true);
}
}
return $return;
}
/**
* Save recent/favorite tables into phpMyAdmin database.
*
* @return true|Message
*/
public function saveToDb()
{
$username = $GLOBALS['cfg']['Server']['user'];
$sql_query
= " REPLACE INTO " . $this->_getPmaTable() . " (`username`, `tables`)" .
" VALUES ('" . $GLOBALS['dbi']->escapeString($username) . "', '"
. $GLOBALS['dbi']->escapeString(
json_encode($this->_tables)
) . "')";
$success = $GLOBALS['dbi']->tryQuery($sql_query, $GLOBALS['controllink']);
if (! $success) {
$error_msg = '';
switch ($this->_tableType) {
case 'recent':
$error_msg = __('Could not save recent table!');
break;
case 'favorite':
$error_msg = __('Could not save favorite table!');
break;
}
$message = Message::error($error_msg);
$message->addMessage('<br /><br />');
$message->addMessage(
Message::rawError(
$GLOBALS['dbi']->getError($GLOBALS['controllink'])
)
);
return $message;
}
return true;
}
/**
* Trim recent.favorite table according to the
* NumRecentTables/NumFavoriteTables configuration.
*
* @return boolean True if trimming occurred
*/
public function trim()
{
$max = max(
$GLOBALS['cfg']['Num' . ucfirst($this->_tableType) . 'Tables'], 0
);
$trimming_occurred = count($this->_tables) > $max;
while (count($this->_tables) > $max) {
array_pop($this->_tables);
}
return $trimming_occurred;
}
/**
* Return HTML ul.
*
* @return string
*/
public function getHtmlList()
{
$html = '';
if (count($this->_tables)) {
if ($this->_tableType == 'recent') {
foreach ($this->_tables as $table) {
$html .= '<li class="warp_link">';
$recent_params = array(
'db' => $table['db'],
'table' => $table['table']
);
$recent_url = 'tbl_recent_favorite.php'
. PMA_URL_getCommon($recent_params);
$html .= '<a href="' . $recent_url . '">`'
. htmlspecialchars($table['db']) . '`.`'
. htmlspecialchars($table['table']) . '`</a>';
$html .= '</li>';
}
} else {
foreach ($this->_tables as $table) {
$html .= '<li class="warp_link">';
$html .= '<a class="ajax favorite_table_anchor" ';
$fav_params = array(
'db' => $table['db'],
'ajax_request' => true,
'favorite_table' => $table['table'],
'remove_favorite' => true
);
$fav_rm_url = 'db_structure.php'
. PMA_URL_getCommon($fav_params);
$html .= 'href="' . $fav_rm_url
. '" title="' . __("Remove from Favorites")
. '" data-favtargetn="'
. md5($table['db'] . "." . $table['table'])
. '" >'
. Util::getIcon('b_favorite.png')
. '</a>';
$fav_params = array(
'db' => $table['db'],
'table' => $table['table']
);
$table_url = 'tbl_recent_favorite.php'
. PMA_URL_getCommon($fav_params);
$html .= '<a href="' . $table_url . '">`'
. htmlspecialchars($table['db']) . '`.`'
. htmlspecialchars($table['table']) . '`</a>';
$html .= '</li>';
}
}
} else {
$html .= '<li class="warp_link">'
. ($this->_tableType == 'recent'
?__('There are no recent tables.')
:__('There are no favorite tables.'))
. '</li>';
}
return $html;
}
/**
* Return HTML.
*
* @return string
*/
public function getHtml()
{
$html = '<div class="drop_list">';
if ($this->_tableType == 'recent') {
$html .= '<span title="' . __('Recent tables')
. '" class="drop_button">'
. __('Recent') . '</span><ul id="pma_recent_list">';
} else {
$html .= '<span title="' . __('Favorite tables')
. '" class="drop_button">'
. __('Favorites') . '</span><ul id="pma_favorite_list">';
}
$html .= $this->getHtmlList();
$html .= '</ul></div>';
return $html;
}
/**
* Add recently used or favorite tables.
*
* @param string $db database name where the table is located
* @param string $table table name
*
* @return true|Message True if success, Message if not
*/
public function add($db, $table)
{
// If table does not exist, do not add._getPmaTable()
if (! $GLOBALS['dbi']->getColumns($db, $table)) {
return true;
}
$table_arr = array();
$table_arr['db'] = $db;
$table_arr['table'] = $table;
// add only if this is new table
if (! isset($this->_tables[0]) || $this->_tables[0] != $table_arr) {
array_unshift($this->_tables, $table_arr);
$this->_tables = array_merge(array_unique($this->_tables, SORT_REGULAR));
$this->trim();
if ($this->_getPmaTable()) {
return $this->saveToDb();
}
}
return true;
}
/**
* Removes recent/favorite tables that don't exist.
*
* @param string $db database
* @param string $table table
*
* @return boolean|Message True if invalid and removed, False if not invalid,
* Message if error while removing
*/
public function removeIfInvalid($db, $table)
{
foreach ($this->_tables as $tbl) {
if ($tbl['db'] == $db && $tbl['table'] == $table) {
// TODO Figure out a better way to find the existence of a table
if (! $GLOBALS['dbi']->getColumns($tbl['db'], $tbl['table'])) {
return $this->remove($tbl['db'], $tbl['table']);
}
}
}
return false;
}
/**
* Remove favorite tables.
*
* @param string $db database name where the table is located
* @param string $table table name
*
* @return true|Message True if success, Message if not
*/
public function remove($db, $table)
{
$table_arr = array();
$table_arr['db'] = $db;
$table_arr['table'] = $table;
foreach ($this->_tables as $key => $value) {
if ($value['db'] == $db && $value['table'] == $table) {
unset($this->_tables[$key]);
}
}
if ($this->_getPmaTable()) {
return $this->saveToDb();
}
return true;
}
/**
* Generate Html for sync Favorite tables anchor. (from localStorage to pmadb)
*
* @return string
*/
public function getHtmlSyncFavoriteTables()
{
$retval = '';
$server_id = $GLOBALS['server'];
if ($server_id == 0) {
return '';
}
// Not to show this once list is synchronized.
$is_synced = isset($_SESSION['tmpval']['favorites_synced'][$server_id]) ?
true : false;
if (!$is_synced) {
$params = array('ajax_request' => true, 'favorite_table' => true,
'sync_favorite_tables' => true);
$url = 'db_structure.php' . PMA_URL_getCommon($params);
$retval = '<a class="hide" id="sync_favorite_tables"';
$retval .= ' href="' . $url . '"></a>';
}
return $retval;
}
/**
* Generate Html to update recent tables.
*
* @return string html
*/
public static function getHtmlUpdateRecentTables()
{
$params = array('ajax_request' => true, 'recent_table' => true);
$url = 'index.php' . PMA_URL_getCommon($params);
$retval = '<a class="hide" id="update_recent_tables"';
$retval .= ' href="' . $url . '"></a>';
return $retval;
}
/**
* Reutrn the name of the configuration storage table
*
* @return string pma table name
*/
private function _getPmaTable()
{
$cfgRelation = PMA_getRelationsParam();
if (! empty($cfgRelation['db'])
&& ! empty($cfgRelation[$this->_tableType])
) {
return Util::backquote($cfgRelation['db']) . "."
. Util::backquote($cfgRelation[$this->_tableType]);
}
return null;
}
}

468
#pma/libraries/Response.php Normal file
View File

@ -0,0 +1,468 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Manages the rendering of pages in PMA
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Singleton class used to manage the rendering of pages in PMA
*
* @package PhpMyAdmin
*/
class Response
{
/**
* Response instance
*
* @access private
* @static
* @var Response
*/
private static $_instance;
/**
* Header instance
*
* @access private
* @var Header
*/
private $_header;
/**
* HTML data to be used in the response
*
* @access private
* @var string
*/
private $_HTML;
/**
* An array of JSON key-value pairs
* to be sent back for ajax requests
*
* @access private
* @var array
*/
private $_JSON;
/**
* PMA\libraries\Footer instance
*
* @access private
* @var Footer
*/
private $_footer;
/**
* Whether we are servicing an ajax request.
* We can't simply use $GLOBALS['is_ajax_request']
* here since it may have not been initialised yet.
*
* @access private
* @var bool
*/
private $_isAjax;
/**
* Whether response object is disabled
*
* @access private
* @var bool
*/
private $_isDisabled;
/**
* Whether we are servicing an ajax request for a page
* that was fired using the generic page handler in JS.
*
* @access private
* @var bool
*/
private $_isAjaxPage;
/**
* Whether there were any errors during the processing of the request
* Only used for ajax responses
*
* @access private
* @var bool
*/
private $_isSuccess;
/**
* Workaround for PHP bug
*
* @access private
* @var string|bool
*/
private $_CWD;
/**
* Creates a new class instance
*/
private function __construct()
{
if (! defined('TESTSUITE')) {
$buffer = OutputBuffering::getInstance();
$buffer->start();
register_shutdown_function(array('PMA\libraries\Response', 'response'));
}
$this->_header = new Header();
$this->_HTML = '';
$this->_JSON = array();
$this->_footer = new Footer();
$this->_isSuccess = true;
$this->_isAjax = false;
$this->_isAjaxPage = false;
$this->_isDisabled = false;
if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) {
$this->_isAjax = true;
}
if (isset($_REQUEST['ajax_page_request'])
&& $_REQUEST['ajax_page_request'] == true
) {
$this->_isAjaxPage = true;
}
$this->_header->setAjax($this->_isAjax);
$this->_footer->setAjax($this->_isAjax);
$this->_CWD = getcwd();
}
/**
* Returns the singleton Response object
*
* @return Response object
*/
public static function getInstance()
{
if (empty(self::$_instance)) {
self::$_instance = new Response();
}
return self::$_instance;
}
/**
* Set the status of an ajax response,
* whether it is a success or an error
*
* @param bool $state Whether the request was successfully processed
*
* @return void
*/
public function setRequestStatus($state)
{
$this->_isSuccess = ($state == true);
}
/**
* Returns true or false depending on whether
* we are servicing an ajax request
*
* @return bool
*/
public function isAjax()
{
return $this->_isAjax;
}
/**
* Returns the path to the current working directory
* Necessary to work around a PHP bug where the CWD is
* reset after the initial script exits
*
* @return string
*/
public function getCWD()
{
return $this->_CWD;
}
/**
* Disables the rendering of the header
* and the footer in responses
*
* @return void
*/
public function disable()
{
$this->_header->disable();
$this->_footer->disable();
$this->_isDisabled = true;
}
/**
* Returns a PMA\libraries\Header object
*
* @return Header
*/
public function getHeader()
{
return $this->_header;
}
/**
* Returns a PMA\libraries\Footer object
*
* @return Footer
*/
public function getFooter()
{
return $this->_footer;
}
/**
* Add HTML code to the response
*
* @param string $content A string to be appended to
* the current output buffer
*
* @return void
*/
public function addHTML($content)
{
if (is_array($content)) {
foreach ($content as $msg) {
$this->addHTML($msg);
}
} elseif ($content instanceof Message) {
$this->_HTML .= $content->getDisplay();
} else {
$this->_HTML .= $content;
}
}
/**
* Add JSON code to the response
*
* @param mixed $json Either a key (string) or an
* array or key-value pairs
* @param mixed $value Null, if passing an array in $json otherwise
* it's a string value to the key
*
* @return void
*/
public function addJSON($json, $value = null)
{
if (is_array($json)) {
foreach ($json as $key => $value) {
$this->addJSON($key, $value);
}
} else {
if ($value instanceof Message) {
$this->_JSON[$json] = $value->getDisplay();
} else {
$this->_JSON[$json] = $value;
}
}
}
/**
* Renders the HTML response text
*
* @return string
*/
private function _getDisplay()
{
// The header may contain nothing at all,
// if its content was already rendered
// and, in this case, the header will be
// in the content part of the request
$retval = $this->_header->getDisplay();
$retval .= $this->_HTML;
$retval .= $this->_footer->getDisplay();
return $retval;
}
/**
* Sends an HTML response to the browser
*
* @return void
*/
private function _htmlResponse()
{
echo $this->_getDisplay();
}
/**
* Sends a JSON response to the browser
*
* @return void
*/
private function _ajaxResponse()
{
/* Avoid wrapping in case we're disabled */
if ($this->_isDisabled) {
echo $this->_getDisplay();
return;
}
if (! isset($this->_JSON['message'])) {
$this->_JSON['message'] = $this->_getDisplay();
} else if ($this->_JSON['message'] instanceof Message) {
$this->_JSON['message'] = $this->_JSON['message']->getDisplay();
}
if ($this->_isSuccess) {
$this->_JSON['success'] = true;
} else {
$this->_JSON['success'] = false;
$this->_JSON['error'] = $this->_JSON['message'];
unset($this->_JSON['message']);
}
if ($this->_isSuccess) {
// Note: the old judge sentence is:
// $this->_isAjaxPage && $this->_isSuccess
// Removal the first, because console need log all queries
$this->addJSON('_title', $this->getHeader()->getTitleTag());
if (isset($GLOBALS['dbi'])) {
$menuHash = $this->getHeader()->getMenu()->getHash();
$this->addJSON('_menuHash', $menuHash);
$hashes = array();
if (isset($_REQUEST['menuHashes'])) {
$hashes = explode('-', $_REQUEST['menuHashes']);
}
if (! in_array($menuHash, $hashes)) {
$this->addJSON(
'_menu',
$this->getHeader()
->getMenu()
->getDisplay()
);
}
}
$this->addJSON('_scripts', $this->getHeader()->getScripts()->getFiles());
$this->addJSON('_selflink', $this->getFooter()->getSelfUrl('unencoded'));
$this->addJSON('_displayMessage', $this->getHeader()->getMessage());
$debug = $this->_footer->getDebugMessage();
if (empty($_REQUEST['no_debug'])
&& mb_strlen($debug)
) {
$this->addJSON('_debug', $debug);
}
$errors = $this->_footer->getErrorMessages();
if (mb_strlen($errors)) {
$this->addJSON('_errors', $errors);
}
$promptPhpErrors = $GLOBALS['error_handler']->hasErrorsForPrompt();
$this->addJSON('_promptPhpErrors', $promptPhpErrors);
if (empty($GLOBALS['error_message'])) {
// set current db, table and sql query in the querywindow
// (this is for the bottom console)
$query = '';
$maxChars = $GLOBALS['cfg']['MaxCharactersInDisplayedSQL'];
if (isset($GLOBALS['sql_query'])
&& mb_strlen($GLOBALS['sql_query']) < $maxChars
) {
$query = $GLOBALS['sql_query'];
}
$this->addJSON(
'_reloadQuerywindow',
array(
'db' => PMA_ifSetOr($GLOBALS['db'], ''),
'table' => PMA_ifSetOr($GLOBALS['table'], ''),
'sql_query' => $query
)
);
if (! empty($GLOBALS['focus_querywindow'])) {
$this->addJSON('_focusQuerywindow', $query);
}
if (! empty($GLOBALS['reload'])) {
$this->addJSON('_reloadNavigation', 1);
}
$this->addJSON('_params', $this->getHeader()->getJsParams());
}
}
// Set the Content-Type header to JSON so that jQuery parses the
// response correctly.
PMA_headerJSON();
$result = json_encode($this->_JSON);
if ($result === false) {
switch (json_last_error()) {
case JSON_ERROR_NONE:
$error = 'No errors';
break;
case JSON_ERROR_DEPTH:
$error = 'Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$error = 'Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$error = 'Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$error = 'Syntax error, malformed JSON';
break;
case JSON_ERROR_UTF8:
$error = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
case JSON_ERROR_RECURSION:
$error = 'One or more recursive references in the value to be encoded';
break;
case JSON_ERROR_INF_OR_NAN:
$error = 'One or more NAN or INF values in the value to be encoded';
break;
case JSON_ERROR_UNSUPPORTED_TYPE:
$error = 'A value of a type that cannot be encoded was given';
default:
$error = 'Unknown error';
break;
}
echo json_encode(
array(
'success' => false,
'error' => 'JSON encoding failed: ' . $error,
)
);
} else {
echo $result;
}
}
/**
* Sends an HTML response to the browser
*
* @static
* @return void
*/
public static function response()
{
$response = Response::getInstance();
chdir($response->getCWD());
$buffer = OutputBuffering::getInstance();
if (empty($response->_HTML)) {
$response->_HTML = $buffer->getContents();
}
if ($response->isAjax()) {
$response->_ajaxResponse();
} else {
$response->_htmlResponse();
}
$buffer->flush();
exit;
}
/**
* Wrapper around PHP's header() function.
*
* @return void
*/
public function header($text)
{
header($text);
}
/**
* Wrapper around PHP's headers_sent() function.
*
* @return bool
*/
public function headersSent()
{
return headers_sent();
}
}

View File

@ -0,0 +1,457 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Saved searches managing
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Saved searches managing
*
* @package PhpMyAdmin
*/
class SavedSearches
{
/**
* Global configuration
* @var array
*/
private $_config = null;
/**
* Id
* @var int|null
*/
private $_id = null;
/**
* Username
* @var string
*/
private $_username = null;
/**
* DB name
* @var string
*/
private $_dbname = null;
/**
* Saved search name
* @var string
*/
private $_searchName = null;
/**
* Setter of id
*
* @param int|null $searchId Id of search
*
* @return static
*/
public function setId($searchId)
{
$searchId = (int)$searchId;
if (empty($searchId)) {
$searchId = null;
}
$this->_id = $searchId;
return $this;
}
/**
* Getter of id
*
* @return int|null
*/
public function getId()
{
return $this->_id;
}
/**
* Setter of searchName
*
* @param string $searchName Saved search name
*
* @return static
*/
public function setSearchName($searchName)
{
$this->_searchName = $searchName;
return $this;
}
/**
* Getter of searchName
*
* @return string
*/
public function getSearchName()
{
return $this->_searchName;
}
/**
* Criterias
* @var array
*/
private $_criterias = null;
/**
* Setter of config
*
* @param array $config Global configuration
*
* @return static
*/
public function setConfig($config)
{
$this->_config = $config;
return $this;
}
/**
* Getter of config
*
* @return array
*/
public function getConfig()
{
return $this->_config;
}
/**
* Setter for criterias
*
* @param array|string $criterias Criterias of saved searches
* @param bool $json Criterias are in JSON format
*
* @return static
*/
public function setCriterias($criterias, $json = false)
{
if (true === $json && is_string($criterias)) {
$this->_criterias = json_decode($criterias, true);
return $this;
}
$aListFieldsToGet = array(
'criteriaColumn',
'criteriaSort',
'criteriaShow',
'criteria',
'criteriaAndOrRow',
'criteriaAndOrColumn',
'rows',
'TableList'
);
$data = array();
$data['criteriaColumnCount'] = count($criterias['criteriaColumn']);
foreach ($aListFieldsToGet as $field) {
if (isset($criterias[$field])) {
$data[$field] = $criterias[$field];
}
}
/* Limit amount of rows */
if (!isset($data['rows'])) {
$data['rows'] = 0;
} else {
$data['rows'] = min(
max(0, intval($data['rows'])),
100
);
}
for ($i = 0; $i <= $data['rows']; $i++) {
$data['Or' . $i] = $criterias['Or' . $i];
}
$this->_criterias = $data;
return $this;
}
/**
* Getter for criterias
*
* @return array
*/
public function getCriterias()
{
return $this->_criterias;
}
/**
* Setter for username
*
* @param string $username Username
*
* @return static
*/
public function setUsername($username)
{
$this->_username = $username;
return $this;
}
/**
* Getter for username
*
* @return string
*/
public function getUsername()
{
return $this->_username;
}
/**
* Setter for DB name
*
* @param string $dbname DB name
*
* @return static
*/
public function setDbname($dbname)
{
$this->_dbname = $dbname;
return $this;
}
/**
* Getter for DB name
*
* @return string
*/
public function getDbname()
{
return $this->_dbname;
}
/**
* Public constructor
*
* @param array $config Global configuration
*/
public function __construct($config)
{
$this->setConfig($config);
}
/**
* Save the search
*
* @return boolean
*/
public function save()
{
if (null == $this->getSearchName()) {
$message = Message::error(
__('Please provide a name for this bookmarked search.')
);
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('fieldWithError', 'searchName');
$response->addJSON('message', $message);
exit;
}
if (null == $this->getUsername()
|| null == $this->getDbname()
|| null == $this->getSearchName()
|| null == $this->getCriterias()
) {
$message = Message::error(
__('Missing information to save the bookmarked search.')
);
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('message', $message);
exit;
}
$savedSearchesTbl
= Util::backquote($this->_config['cfgRelation']['db']) . "."
. Util::backquote($this->_config['cfgRelation']['savedsearches']);
//If it's an insert.
if (null === $this->getId()) {
$wheres = array(
"search_name = '" . $GLOBALS['dbi']->escapeString($this->getSearchName())
. "'"
);
$existingSearches = $this->getList($wheres);
if (!empty($existingSearches)) {
$message = Message::error(
__('An entry with this name already exists.')
);
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('fieldWithError', 'searchName');
$response->addJSON('message', $message);
exit;
}
$sqlQuery = "INSERT INTO " . $savedSearchesTbl
. "(`username`, `db_name`, `search_name`, `search_data`)"
. " VALUES ("
. "'" . $GLOBALS['dbi']->escapeString($this->getUsername()) . "',"
. "'" . $GLOBALS['dbi']->escapeString($this->getDbname()) . "',"
. "'" . $GLOBALS['dbi']->escapeString($this->getSearchName()) . "',"
. "'" . $GLOBALS['dbi']->escapeString(json_encode($this->getCriterias()))
. "')";
$result = (bool)PMA_queryAsControlUser($sqlQuery);
if (!$result) {
return false;
}
$this->setId($GLOBALS['dbi']->insertId());
return true;
}
//Else, it's an update.
$wheres = array(
"id != " . $this->getId(),
"search_name = '" . $GLOBALS['dbi']->escapeString($this->getSearchName()) . "'"
);
$existingSearches = $this->getList($wheres);
if (!empty($existingSearches)) {
$message = Message::error(
__('An entry with this name already exists.')
);
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('fieldWithError', 'searchName');
$response->addJSON('message', $message);
exit;
}
$sqlQuery = "UPDATE " . $savedSearchesTbl
. "SET `search_name` = '"
. $GLOBALS['dbi']->escapeString($this->getSearchName()) . "', "
. "`search_data` = '"
. $GLOBALS['dbi']->escapeString(json_encode($this->getCriterias())) . "' "
. "WHERE id = " . $this->getId();
return (bool)PMA_queryAsControlUser($sqlQuery);
}
/**
* Delete the search
*
* @return boolean
*/
public function delete()
{
if (null == $this->getId()) {
$message = Message::error(
__('Missing information to delete the search.')
);
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('fieldWithError', 'searchId');
$response->addJSON('message', $message);
exit;
}
$savedSearchesTbl
= Util::backquote($this->_config['cfgRelation']['db']) . "."
. Util::backquote($this->_config['cfgRelation']['savedsearches']);
$sqlQuery = "DELETE FROM " . $savedSearchesTbl
. "WHERE id = '" . $GLOBALS['dbi']->escapeString($this->getId()) . "'";
return (bool)PMA_queryAsControlUser($sqlQuery);
}
/**
* Load the current search from an id.
*
* @return bool Success
*/
public function load()
{
if (null == $this->getId()) {
$message = Message::error(
__('Missing information to load the search.')
);
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('fieldWithError', 'searchId');
$response->addJSON('message', $message);
exit;
}
$savedSearchesTbl = Util::backquote($this->_config['cfgRelation']['db'])
. "."
. Util::backquote($this->_config['cfgRelation']['savedsearches']);
$sqlQuery = "SELECT id, search_name, search_data "
. "FROM " . $savedSearchesTbl . " "
. "WHERE id = '" . $GLOBALS['dbi']->escapeString($this->getId()) . "' ";
$resList = PMA_queryAsControlUser($sqlQuery);
if (false === ($oneResult = $GLOBALS['dbi']->fetchArray($resList))) {
$message = Message::error(__('Error while loading the search.'));
$response = Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('fieldWithError', 'searchId');
$response->addJSON('message', $message);
exit;
}
$this->setSearchName($oneResult['search_name'])
->setCriterias($oneResult['search_data'], true);
return true;
}
/**
* Get the list of saved searches of a user on a DB
*
* @param string[] $wheres List of filters
*
* @return array List of saved searches or empty array on failure
*/
public function getList(array $wheres = array())
{
if (null == $this->getUsername()
|| null == $this->getDbname()
) {
return array();
}
$savedSearchesTbl = Util::backquote($this->_config['cfgRelation']['db'])
. "."
. Util::backquote($this->_config['cfgRelation']['savedsearches']);
$sqlQuery = "SELECT id, search_name "
. "FROM " . $savedSearchesTbl . " "
. "WHERE "
. "username = '" . $GLOBALS['dbi']->escapeString($this->getUsername()) . "' "
. "AND db_name = '" . $GLOBALS['dbi']->escapeString($this->getDbname()) . "' ";
foreach ($wheres as $where) {
$sqlQuery .= "AND " . $where . " ";
}
$sqlQuery .= "order by search_name ASC ";
$resList = PMA_queryAsControlUser($sqlQuery);
$list = array();
while ($oneResult = $GLOBALS['dbi']->fetchArray($resList)) {
$list[$oneResult['id']] = $oneResult['search_name'];
}
return $list;
}
}

259
#pma/libraries/Scripts.php Normal file
View File

@ -0,0 +1,259 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* JavaScript management
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Collects information about which JavaScript
* files and objects are necessary to render
* the page and generates the relevant code.
*
* @package PhpMyAdmin
*/
class Scripts
{
/**
* An array of SCRIPT tags
*
* @access private
* @var array of strings
*/
private $_files;
/**
* An array of discrete javascript code snippets
*
* @access private
* @var array of strings
*/
private $_code;
/**
* Returns HTML code to include javascript file.
*
* @param array $files The list of js file to include
*
* @return string HTML code for javascript inclusion.
*/
private function _includeFiles($files)
{
$first_dynamic_scripts = "";
$dynamic_scripts = "";
$scripts = array();
$separator = PMA_URL_getArgSeparator();
foreach ($files as $value) {
if (mb_strpos($value['filename'], "?") !== false) {
$file_name = $value['filename'] . $separator
. Header::getVersionParameter();
if ($value['before_statics'] === true) {
$first_dynamic_scripts
.= "<script data-cfasync='false' type='text/javascript' "
. "src='js/" . $file_name . "'></script>";
} else {
$dynamic_scripts .= "<script data-cfasync='false' "
. "type='text/javascript' src='js/" . $file_name
. "'></script>";
}
continue;
}
$include = true;
if ($value['conditional_ie'] !== false
&& PMA_USR_BROWSER_AGENT === 'IE'
) {
if ($value['conditional_ie'] === true) {
$include = true;
} else if ($value['conditional_ie'] == PMA_USR_BROWSER_VER) {
$include = true;
} else {
$include = false;
}
}
if ($include) {
$scripts[] = "scripts%5B%5D=" . $value['filename'];
}
}
$separator = PMA_URL_getArgSeparator();
$static_scripts = '';
// Using chunks of 10 files to avoid too long URLs
// as some servers are set to 512 bytes URL limit
$script_chunks = array_chunk($scripts, 10);
foreach ($script_chunks as $script_chunk) {
$url = 'js/get_scripts.js.php?'
. implode($separator, $script_chunk)
. $separator . Header::getVersionParameter();
$static_scripts .= sprintf(
'<script data-cfasync="false" type="text/javascript" src="%s">' .
'</script>',
htmlspecialchars($url)
);
}
return $first_dynamic_scripts . $static_scripts . $dynamic_scripts;
}
/**
* Generates new Scripts objects
*
*/
public function __construct()
{
$this->_files = array();
$this->_code = '';
}
/**
* Adds a new file to the list of scripts
*
* @param string $filename The name of the file to include
* @param bool $conditional_ie Whether to wrap the script tag in
* conditional comments for IE
* @param bool $before_statics Whether this dynamic script should be
* included before the static ones
*
* @return void
*/
public function addFile(
$filename,
$conditional_ie = false,
$before_statics = false
) {
$hash = md5($filename);
if (!empty($this->_files[$hash])) {
return;
}
$has_onload = $this->_eventBlacklist($filename);
$this->_files[$hash] = array(
'has_onload' => $has_onload,
'filename' => $filename,
'conditional_ie' => $conditional_ie,
'before_statics' => $before_statics
);
}
/**
* Add new files to the list of scripts
*
* @param array $filelist The array of file names
* @param bool $conditional_ie Whether to wrap the script tag in
* conditional comments for IE
*
* @return void
*/
public function addFiles($filelist, $conditional_ie = false)
{
foreach ($filelist as $filename) {
$this->addFile($filename, $conditional_ie);
}
}
/**
* Determines whether to fire up an onload event for a file
*
* @param string $filename The name of the file to be checked
* against the blacklist
*
* @return int 1 to fire up the event, 0 not to
*/
private function _eventBlacklist($filename)
{
if (strpos($filename, 'jquery') !== false
|| strpos($filename, 'codemirror') !== false
|| strpos($filename, 'messages.php') !== false
|| strpos($filename, 'ajax.js') !== false
|| strpos($filename, 'get_image.js.php') !== false
|| strpos($filename, 'cross_framing_protection.js') !== false
) {
return 0;
}
return 1;
}
/**
* Adds a new code snippet to the code to be executed
*
* @param string $code The JS code to be added
*
* @return void
*/
public function addCode($code)
{
$this->_code .= "$code\n";
}
/**
* Returns a list with filenames and a flag to indicate
* whether to register onload events for this file
*
* @return array
*/
public function getFiles()
{
$retval = array();
foreach ($this->_files as $file) {
//If filename contains a "?", continue.
if (strpos($file['filename'], "?") !== false) {
continue;
}
if (! $file['conditional_ie'] || PMA_USR_BROWSER_AGENT == 'IE') {
$retval[] = array(
'name' => $file['filename'],
'fire' => $file['has_onload']
);
}
}
return $retval;
}
/**
* Renders all the JavaScript file inclusions, code and events
*
* @return string
*/
public function getDisplay()
{
$retval = '';
if (count($this->_files) > 0) {
$retval .= $this->_includeFiles(
$this->_files
);
}
$code = 'AJAX.scriptHandler';
foreach ($this->_files as $file) {
$code .= sprintf(
'.add("%s",%d)',
PMA_escapeJsString($file['filename']),
$file['has_onload'] ? 1 : 0
);
}
$code .= ';';
$this->addCode($code);
$code = '$(function() {';
foreach ($this->_files as $file) {
if ($file['has_onload']) {
$code .= 'AJAX.fireOnload("';
$code .= PMA_escapeJsString($file['filename']);
$code .= '");';
}
}
$code .= '});';
$this->addCode($code);
$retval .= '<script data-cfasync="false" type="text/javascript">';
$retval .= "// <![CDATA[\n";
$retval .= $this->_code;
$retval .= '// ]]>';
$retval .= '</script>';
return $retval;
}
}

View File

@ -0,0 +1,502 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* ServerStatusData class
* Used by server_status_*.php pages
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* This class provides data about the server status
*
* All properties of the class are read-only
*
* TODO: Use lazy initialisation for some of the properties
* since not all of the server_status_*.php pages need
* all the data that this class provides.
*
* @package PhpMyAdmin
*/
class ServerStatusData
{
public $status;
public $sections;
public $variables;
public $used_queries;
public $allocationMap;
public $links;
public $db_isLocal;
public $section;
public $sectionUsed;
public $selfUrl;
public $dataLoaded;
/**
* An empty setter makes the above properties read-only
*
* @param string $a key
* @param mixed $b value
*
* @return void
*/
public function __set($a, $b)
{
// Discard everything
}
/**
* Gets the allocations for constructor
*
* @return array
*/
private function _getAllocations()
{
return array(
// variable name => section
// variable names match when they begin with the given string
'Com_' => 'com',
'Innodb_' => 'innodb',
'Ndb_' => 'ndb',
'Handler_' => 'handler',
'Qcache_' => 'qcache',
'Threads_' => 'threads',
'Slow_launch_threads' => 'threads',
'Binlog_cache_' => 'binlog_cache',
'Created_tmp_' => 'created_tmp',
'Key_' => 'key',
'Delayed_' => 'delayed',
'Not_flushed_delayed_rows' => 'delayed',
'Flush_commands' => 'query',
'Last_query_cost' => 'query',
'Slow_queries' => 'query',
'Queries' => 'query',
'Prepared_stmt_count' => 'query',
'Select_' => 'select',
'Sort_' => 'sort',
'Open_tables' => 'table',
'Opened_tables' => 'table',
'Open_table_definitions' => 'table',
'Opened_table_definitions' => 'table',
'Table_locks_' => 'table',
'Rpl_status' => 'repl',
'Slave_' => 'repl',
'Tc_' => 'tc',
'Ssl_' => 'ssl',
'Open_files' => 'files',
'Open_streams' => 'files',
'Opened_files' => 'files',
);
}
/**
* Gets the sections for constructor
*
* @return array
*/
private function _getSections()
{
return array(
// section => section name (description)
'com' => 'Com',
'query' => __('SQL query'),
'innodb' => 'InnoDB',
'ndb' => 'NDB',
'handler' => __('Handler'),
'qcache' => __('Query cache'),
'threads' => __('Threads'),
'binlog_cache' => __('Binary log'),
'created_tmp' => __('Temporary data'),
'delayed' => __('Delayed inserts'),
'key' => __('Key cache'),
'select' => __('Joins'),
'repl' => __('Replication'),
'sort' => __('Sorting'),
'table' => __('Tables'),
'tc' => __('Transaction coordinator'),
'files' => __('Files'),
'ssl' => 'SSL',
'other' => __('Other')
);
}
/**
* Gets the links for constructor
*
* @return array
*/
private function _getLinks()
{
$links = array();
// variable or section name => (name => url)
$links['table'][__('Flush (close) all tables')] = $this->selfUrl
. PMA_URL_getCommon(
array(
'flush' => 'TABLES'
)
);
$links['table'][__('Show open tables')]
= 'sql.php' . PMA_URL_getCommon(
array(
'sql_query' => 'SHOW OPEN TABLES',
'goto' => $this->selfUrl,
)
);
if ($GLOBALS['replication_info']['master']['status']) {
$links['repl'][__('Show slave hosts')]
= 'sql.php' . PMA_URL_getCommon(
array(
'sql_query' => 'SHOW SLAVE HOSTS',
'goto' => $this->selfUrl,
)
);
$links['repl'][__('Show master status')] = '#replication_master';
}
if ($GLOBALS['replication_info']['slave']['status']) {
$links['repl'][__('Show slave status')] = '#replication_slave';
}
$links['repl']['doc'] = 'replication';
$links['qcache'][__('Flush query cache')]
= $this->selfUrl
. PMA_URL_getCommon(
array(
'flush' => 'QUERY CACHE'
)
);
$links['qcache']['doc'] = 'query_cache';
$links['threads']['doc'] = 'mysql_threads';
$links['key']['doc'] = 'myisam_key_cache';
$links['binlog_cache']['doc'] = 'binary_log';
$links['Slow_queries']['doc'] = 'slow_query_log';
$links['innodb'][__('Variables')]
= 'server_engines.php?engine=InnoDB&amp;'
. PMA_URL_getCommon(array(), 'html', '');
$links['innodb'][__('InnoDB Status')]
= 'server_engines.php'
. PMA_URL_getCommon(
array(
'engine' => 'InnoDB',
'page' => 'Status'
)
);
$links['innodb']['doc'] = 'innodb';
return($links);
}
/**
* Calculate some values
*
* @param array $server_status contains results of SHOW GLOBAL STATUS
* @param array $server_variables contains results of SHOW GLOBAL VARIABLES
*
* @return array $server_status
*/
private function _calculateValues($server_status, $server_variables)
{
// Key_buffer_fraction
if (isset($server_status['Key_blocks_unused'])
&& isset($server_variables['key_cache_block_size'])
&& isset($server_variables['key_buffer_size'])
&& $server_variables['key_buffer_size'] != 0
) {
$server_status['Key_buffer_fraction_%']
= 100
- $server_status['Key_blocks_unused']
* $server_variables['key_cache_block_size']
/ $server_variables['key_buffer_size']
* 100;
} elseif (isset($server_status['Key_blocks_used'])
&& isset($server_variables['key_buffer_size'])
&& $server_variables['key_buffer_size'] != 0
) {
$server_status['Key_buffer_fraction_%']
= $server_status['Key_blocks_used']
* 1024
/ $server_variables['key_buffer_size'];
}
// Ratio for key read/write
if (isset($server_status['Key_writes'])
&& isset($server_status['Key_write_requests'])
&& $server_status['Key_write_requests'] > 0
) {
$key_writes = $server_status['Key_writes'];
$key_write_requests = $server_status['Key_write_requests'];
$server_status['Key_write_ratio_%']
= 100 * $key_writes / $key_write_requests;
}
if (isset($server_status['Key_reads'])
&& isset($server_status['Key_read_requests'])
&& $server_status['Key_read_requests'] > 0
) {
$key_reads = $server_status['Key_reads'];
$key_read_requests = $server_status['Key_read_requests'];
$server_status['Key_read_ratio_%']
= 100 * $key_reads / $key_read_requests;
}
// Threads_cache_hitrate
if (isset($server_status['Threads_created'])
&& isset($server_status['Connections'])
&& $server_status['Connections'] > 0
) {
$server_status['Threads_cache_hitrate_%']
= 100 - $server_status['Threads_created']
/ $server_status['Connections'] * 100;
}
return $server_status;
}
/**
* Sort variables into arrays
*
* @param array $server_status contains results of SHOW GLOBAL STATUS
* @param array $allocations allocations for sections
* @param array $allocationMap map variables to their section
* @param array $sectionUsed is a section used?
* @param array $used_queries used queries
*
* @return array ($allocationMap, $sectionUsed, $used_queries)
*/
private function _sortVariables(
$server_status, $allocations, $allocationMap, $sectionUsed,
$used_queries
) {
foreach ($server_status as $name => $value) {
$section_found = false;
foreach ($allocations as $filter => $section) {
if (mb_strpos($name, $filter) !== false) {
$allocationMap[$name] = $section;
$sectionUsed[$section] = true;
$section_found = true;
if ($section == 'com' && $value > 0) {
$used_queries[$name] = $value;
}
break; // Only exits inner loop
}
}
if (! $section_found) {
$allocationMap[$name] = 'other';
$sectionUsed['other'] = true;
}
}
return array($allocationMap, $sectionUsed, $used_queries);
}
/**
* Constructor
*/
public function __construct()
{
$this->selfUrl = basename($GLOBALS['PMA_PHP_SELF']);
// get status from server
$server_status_result = $GLOBALS['dbi']->tryQuery('SHOW GLOBAL STATUS');
$server_status = array();
if ($server_status_result === false) {
$this->dataLoaded = false;
} else {
$this->dataLoaded = true;
while ($arr = $GLOBALS['dbi']->fetchRow($server_status_result)) {
$server_status[$arr[0]] = $arr[1];
}
$GLOBALS['dbi']->freeResult($server_status_result);
}
// for some calculations we require also some server settings
$server_variables = $GLOBALS['dbi']->fetchResult(
'SHOW GLOBAL VARIABLES', 0, 1
);
// cleanup of some deprecated values
$server_status = self::cleanDeprecated($server_status);
// calculate some values
$server_status = $this->_calculateValues(
$server_status, $server_variables
);
// split variables in sections
$allocations = $this->_getAllocations();
$sections = $this->_getSections();
// define some needful links/commands
$links = $this->_getLinks();
// Variable to contain all com_ variables (query statistics)
$used_queries = array();
// Variable to map variable names to their respective section name
// (used for js category filtering)
$allocationMap = array();
// Variable to mark used sections
$sectionUsed = array();
// sort vars into arrays
list(
$allocationMap, $sectionUsed, $used_queries
) = $this->_sortVariables(
$server_status, $allocations, $allocationMap, $sectionUsed,
$used_queries
);
// admin commands are not queries (e.g. they include COM_PING,
// which is excluded from $server_status['Questions'])
unset($used_queries['Com_admin_commands']);
// Set all class properties
$this->db_isLocal = false;
$serverHostToLower = mb_strtolower(
$GLOBALS['cfg']['Server']['host']
);
if ($serverHostToLower === 'localhost'
|| $GLOBALS['cfg']['Server']['host'] === '127.0.0.1'
|| $GLOBALS['cfg']['Server']['host'] === '::1'
) {
$this->db_isLocal = true;
}
$this->status = $server_status;
$this->sections = $sections;
$this->variables = $server_variables;
$this->used_queries = $used_queries;
$this->allocationMap = $allocationMap;
$this->links = $links;
$this->sectionUsed = $sectionUsed;
}
/**
* cleanup of some deprecated values
*
* @param array $server_status status array to process
*
* @return array
*/
public static function cleanDeprecated($server_status)
{
$deprecated = array(
'Com_prepare_sql' => 'Com_stmt_prepare',
'Com_execute_sql' => 'Com_stmt_execute',
'Com_dealloc_sql' => 'Com_stmt_close',
);
foreach ($deprecated as $old => $new) {
if (isset($server_status[$old]) && isset($server_status[$new])) {
unset($server_status[$old]);
}
}
return $server_status;
}
/**
* Generates menu HTML
*
* @return string
*/
public function getMenuHtml()
{
$url_params = PMA_URL_getCommon();
$items = array(
array(
'name' => __('Server'),
'url' => 'server_status.php'
),
array(
'name' => __('Processes'),
'url' => 'server_status_processes.php'
),
array(
'name' => __('Query statistics'),
'url' => 'server_status_queries.php'
),
array(
'name' => __('All status variables'),
'url' => 'server_status_variables.php'
),
array(
'name' => __('Monitor'),
'url' => 'server_status_monitor.php'
),
array(
'name' => __('Advisor'),
'url' => 'server_status_advisor.php'
)
);
$retval = '<ul id="topmenu2">';
foreach ($items as $item) {
$class = '';
if ($item['url'] === $this->selfUrl) {
$class = ' class="tabactive"';
}
$retval .= '<li>';
$retval .= '<a' . $class;
$retval .= ' href="' . $item['url'] . $url_params . '">';
$retval .= $item['name'];
$retval .= '</a>';
$retval .= '</li>';
}
$retval .= '</ul>';
$retval .= '<div class="clearfloat"></div>';
return $retval;
}
/**
* Builds a <select> list for refresh rates
*
* @param string $name Name of select
* @param int $defaultRate Currently chosen rate
* @param array $refreshRates List of refresh rates
*
* @return string
*/
public static function getHtmlForRefreshList($name,
$defaultRate = 5,
$refreshRates = Array(1, 2, 5, 10, 20, 40, 60, 120, 300, 600)
) {
$return = '<select name="' . $name . '" id="id_' . $name
. '" class="refreshRate">';
foreach ($refreshRates as $rate) {
$selected = ($rate == $defaultRate)?' selected="selected"':'';
$return .= '<option value="' . $rate . '"' . $selected . '>';
if ($rate < 60) {
$return .= sprintf(
_ngettext('%d second', '%d seconds', $rate), $rate
);
} else {
$rate = $rate / 60;
$return .= sprintf(
_ngettext('%d minute', '%d minutes', $rate), $rate
);
}
$return .= '</option>';
}
$return .= '</select>';
return $return;
}
}

View File

@ -0,0 +1,463 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Library for extracting information about the available storage engines
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* defines
*/
use PMA\libraries\engines\Bdb;
use PMA\libraries\engines\Berkeleydb;
use PMA\libraries\engines\Binlog;
use PMA\libraries\engines\Innobase;
use PMA\libraries\engines\Innodb;
use PMA\libraries\engines\Memory;
use PMA\libraries\engines\Merge;
use PMA\libraries\engines\Mrg_Myisam;
use PMA\libraries\engines\Myisam;
use PMA\libraries\engines\Ndbcluster;
use PMA\libraries\engines\Pbxt;
use PMA\libraries\engines\Performance_Schema;
define('PMA_ENGINE_SUPPORT_NO', 0);
define('PMA_ENGINE_SUPPORT_DISABLED', 1);
define('PMA_ENGINE_SUPPORT_YES', 2);
define('PMA_ENGINE_SUPPORT_DEFAULT', 3);
define('PMA_ENGINE_DETAILS_TYPE_PLAINTEXT', 0);
define('PMA_ENGINE_DETAILS_TYPE_SIZE', 1);
define('PMA_ENGINE_DETAILS_TYPE_NUMERIC', 2); //Has no effect yet...
define('PMA_ENGINE_DETAILS_TYPE_BOOLEAN', 3); // 'ON' or 'OFF'
/**
* Base Storage Engine Class
*
* @package PhpMyAdmin
*/
class StorageEngine
{
/**
* @var string engine name
*/
var $engine = 'dummy';
/**
* @var string engine title/description
*/
var $title = 'PMA Dummy Engine Class';
/**
* @var string engine lang description
*/
var $comment
= 'If you read this text inside phpMyAdmin, something went wrong...';
/**
* @var integer engine supported by current server
*/
var $support = PMA_ENGINE_SUPPORT_NO;
/**
* Constructor
*
* @param string $engine The engine ID
*/
public function __construct($engine)
{
$storage_engines = StorageEngine::getStorageEngines();
if (! empty($storage_engines[$engine])) {
$this->engine = $engine;
$this->title = $storage_engines[$engine]['Engine'];
$this->comment = (isset($storage_engines[$engine]['Comment'])
? $storage_engines[$engine]['Comment']
: '');
switch ($storage_engines[$engine]['Support']) {
case 'DEFAULT':
$this->support = PMA_ENGINE_SUPPORT_DEFAULT;
break;
case 'YES':
$this->support = PMA_ENGINE_SUPPORT_YES;
break;
case 'DISABLED':
$this->support = PMA_ENGINE_SUPPORT_DISABLED;
break;
case 'NO':
default:
$this->support = PMA_ENGINE_SUPPORT_NO;
}
}
}
/**
* Returns array of storage engines
*
* @static
* @staticvar array $storage_engines storage engines
* @access public
* @return string[] array of storage engines
*/
static public function getStorageEngines()
{
static $storage_engines = null;
if (null == $storage_engines) {
$storage_engines
= $GLOBALS['dbi']->fetchResult('SHOW STORAGE ENGINES', 'Engine');
if (PMA_MYSQL_INT_VERSION >= 50708) {
$disabled = Util::cacheGet(
'disabled_storage_engines',
function () {
return $GLOBALS['dbi']->fetchValue(
'SELECT @@disabled_storage_engines'
);
}
);
foreach (explode(",", $disabled) as $engine) {
if (isset($storage_engines[$engine])) {
$storage_engines[$engine]['Support'] = 'DISABLED';
}
}
}
}
return $storage_engines;
}
/**
* Returns HTML code for storage engine select box
*
* @param string $name The name of the select form element
* @param string $id The ID of the form field
* @param string $selected The selected engine
* @param boolean $offerUnavailableEngines Should unavailable storage
* engines be offered?
* @param boolean $addEmpty Whether to provide empty option
*
* @static
* @return string html selectbox
*/
static public function getHtmlSelect(
$name = 'engine', $id = null,
$selected = null, $offerUnavailableEngines = false,
$addEmpty = false
) {
$selected = mb_strtolower($selected);
$output = '<select name="' . $name . '"'
. (empty($id) ? '' : ' id="' . $id . '"') . '>' . "\n";
if ($addEmpty) {
$output .= '<option value=""></option>';
}
foreach (StorageEngine::getStorageEngines() as $key => $details) {
// Don't show PERFORMANCE_SCHEMA engine (MySQL 5.5)
if (! $offerUnavailableEngines
&& ($details['Support'] == 'NO'
|| $details['Support'] == 'DISABLED'
|| $details['Engine'] == 'PERFORMANCE_SCHEMA')
) {
continue;
}
$output .= ' <option value="' . htmlspecialchars($key) . '"'
. (empty($details['Comment'])
? '' : ' title="' . htmlspecialchars($details['Comment']) . '"')
. (mb_strtolower($key) == $selected
|| (empty($selected) && $details['Support'] == 'DEFAULT' && ! $addEmpty)
? ' selected="selected"' : '')
. '>' . "\n"
. ' ' . htmlspecialchars($details['Engine']) . "\n"
. ' </option>' . "\n";
}
$output .= '</select>' . "\n";
return $output;
}
/**
* Loads the corresponding engine plugin, if available.
*
* @param string $engine The engine ID
*
* @return StorageEngine The engine plugin
* @static
*/
static public function getEngine($engine)
{
switch(strtolower($engine)) {
case 'bdb':
return new Bdb($engine);
case 'berkeleydb':
return new Berkeleydb($engine);
case 'binlog':
return new Binlog($engine);
case 'innobase':
return new Innobase($engine);
case 'innodb':
return new Innodb($engine);
case 'memory':
return new Memory($engine);
case 'merge':
return new Merge($engine);
case 'mrg_myisam':
return new Mrg_Myisam($engine);
case 'myisam':
return new Myisam($engine);
case 'ndbcluster':
return new Ndbcluster($engine);
case 'pbxt':
return new Pbxt($engine);
case 'performance_schema':
return new Performance_Schema($engine);
default:
return new StorageEngine($engine);
}
}
/**
* Returns true if given engine name is supported/valid, otherwise false
*
* @param string $engine name of engine
*
* @static
* @return boolean whether $engine is valid or not
*/
static public function isValid($engine)
{
if ($engine == "PBMS") {
return true;
}
$storage_engines = StorageEngine::getStorageEngines();
return isset($storage_engines[$engine]);
}
/**
* Returns as HTML table of the engine's server variables
*
* @return string The table that was generated based on the retrieved
* information
*/
public function getHtmlVariables()
{
$odd_row = false;
$ret = '';
foreach ($this->getVariablesStatus() as $details) {
$ret .= '<tr class="' . ($odd_row ? 'odd' : 'even') . '">' . "\n"
. ' <td>' . "\n";
if (! empty($details['desc'])) {
$ret .= ' '
. Util::showHint($details['desc'])
. "\n";
}
$ret .= ' </td>' . "\n"
. ' <th>' . htmlspecialchars($details['title']) . '</th>'
. "\n"
. ' <td class="value">';
switch ($details['type']) {
case PMA_ENGINE_DETAILS_TYPE_SIZE:
$parsed_size = $this->resolveTypeSize($details['value']);
$ret .= $parsed_size[0] . '&nbsp;' . $parsed_size[1];
unset($parsed_size);
break;
case PMA_ENGINE_DETAILS_TYPE_NUMERIC:
$ret .= Util::formatNumber($details['value']) . ' ';
break;
default:
$ret .= htmlspecialchars($details['value']) . ' ';
}
$ret .= '</td>' . "\n"
. '</tr>' . "\n";
$odd_row = ! $odd_row;
}
if (! $ret) {
$ret = '<p>' . "\n"
. ' '
. __(
'There is no detailed status information available for this '
. 'storage engine.'
)
. "\n"
. '</p>' . "\n";
} else {
$ret = '<table class="data">' . "\n" . $ret . '</table>' . "\n";
}
return $ret;
}
/**
* Returns the engine specific handling for
* PMA_ENGINE_DETAILS_TYPE_SIZE type variables.
*
* This function should be overridden when
* PMA_ENGINE_DETAILS_TYPE_SIZE type needs to be
* handled differently for a particular engine.
*
* @param integer $value Value to format
*
* @return string the formatted value and its unit
*/
public function resolveTypeSize($value)
{
return Util::formatByteDown($value);
}
/**
* Returns array with detailed info about engine specific server variables
*
* @return array array with detailed info about specific engine server variables
*/
public function getVariablesStatus()
{
$variables = $this->getVariables();
$like = $this->getVariablesLikePattern();
if ($like) {
$like = " LIKE '" . $like . "' ";
} else {
$like = '';
}
$mysql_vars = array();
$sql_query = 'SHOW GLOBAL VARIABLES ' . $like . ';';
$res = $GLOBALS['dbi']->query($sql_query);
while ($row = $GLOBALS['dbi']->fetchAssoc($res)) {
if (isset($variables[$row['Variable_name']])) {
$mysql_vars[$row['Variable_name']]
= $variables[$row['Variable_name']];
} elseif (! $like
&& mb_strpos(mb_strtolower($row['Variable_name']), mb_strtolower($this->engine)) !== 0
) {
continue;
}
$mysql_vars[$row['Variable_name']]['value'] = $row['Value'];
if (empty($mysql_vars[$row['Variable_name']]['title'])) {
$mysql_vars[$row['Variable_name']]['title'] = $row['Variable_name'];
}
if (! isset($mysql_vars[$row['Variable_name']]['type'])) {
$mysql_vars[$row['Variable_name']]['type']
= PMA_ENGINE_DETAILS_TYPE_PLAINTEXT;
}
}
$GLOBALS['dbi']->freeResult($res);
return $mysql_vars;
}
/**
* Reveals the engine's title
*
* @return string The title
*/
public function getTitle()
{
return $this->title;
}
/**
* Fetches the server's comment about this engine
*
* @return string The comment
*/
public function getComment()
{
return $this->comment;
}
/**
* Information message on whether this storage engine is supported
*
* @return string The localized message.
*/
public function getSupportInformationMessage()
{
switch ($this->support) {
case PMA_ENGINE_SUPPORT_DEFAULT:
$message = __('%s is the default storage engine on this MySQL server.');
break;
case PMA_ENGINE_SUPPORT_YES:
$message = __('%s is available on this MySQL server.');
break;
case PMA_ENGINE_SUPPORT_DISABLED:
$message = __('%s has been disabled for this MySQL server.');
break;
case PMA_ENGINE_SUPPORT_NO:
default:
$message = __(
'This MySQL server does not support the %s storage engine.'
);
}
return sprintf($message, htmlspecialchars($this->title));
}
/**
* Generates a list of MySQL variables that provide information about this
* engine. This function should be overridden when extending this class
* for a particular engine.
*
* @return array The list of variables.
*/
public function getVariables()
{
return array();
}
/**
* Returns string with filename for the MySQL helppage
* about this storage engine
*
* @return string MySQL help page filename
*/
public function getMysqlHelpPage()
{
return $this->engine . '-storage-engine';
}
/**
* Returns the pattern to be used in the query for SQL variables
* related to the storage engine
*
* @return string SQL query LIKE pattern
*/
public function getVariablesLikePattern()
{
return '';
}
/**
* Returns a list of available information pages with labels
*
* @return string[] The list
*/
public function getInfoPages()
{
return array();
}
/**
* Generates the requested information page
*
* @param string $id page id
*
* @return string html output
*/
public function getPage($id)
{
if (! array_key_exists($id, $this->getInfoPages())) {
return '';
}
$id = 'getPage' . $id;
return $this->$id();
}
}

View File

@ -0,0 +1,180 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Library for extracting information about the sub-partitions
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Represents a sub partition of a table
*
* @package PhpMyAdmin
*/
class SubPartition
{
/**
* @var string the database
*/
protected $db;
/**
* @var string the table
*/
protected $table;
/**
* @var string partition name
*/
protected $name;
/**
* @var integer ordinal
*/
protected $ordinal;
/**
* @var string partition method
*/
protected $method;
/**
* @var string partition expression
*/
protected $expression;
/**
* @var integer no of table rows in the partition
*/
protected $rows;
/**
* @var integer data length
*/
protected $dataLength;
/**
* @var integer index length
*/
protected $indexLength;
/**
* @var string partition comment
*/
protected $comment;
/**
* Constructs a partition
*
* @param array $row fetched row from information_schema.PARTITIONS
*/
public function __construct($row)
{
$this->db = $row['TABLE_SCHEMA'];
$this->table = $row['TABLE_NAME'];
$this->loadData($row);
}
/**
* Loads data from the fetched row from information_schema.PARTITIONS
*
* @param array $row fetched row
*
* @return void
*/
protected function loadData($row)
{
$this->name = $row['SUBPARTITION_NAME'];
$this->ordinal = $row['SUBPARTITION_ORDINAL_POSITION'];
$this->method = $row['SUBPARTITION_METHOD'];
$this->expression = $row['SUBPARTITION_EXPRESSION'];
$this->loadCommonData($row);
}
/**
* Loads some data that is common to both partitions and sub partitions
*
* @param array $row fetched row
*
* @return void
*/
protected function loadCommonData($row)
{
$this->rows = $row['TABLE_ROWS'];
$this->dataLength = $row['DATA_LENGTH'];
$this->indexLength = $row['INDEX_LENGTH'];
$this->comment = $row['PARTITION_COMMENT'];
}
/**
* Return the partition name
*
* @return string partition name
*/
public function getName()
{
return $this->name;
}
/**
* Return the ordinal of the partition
*
* @return number the ordinal
*/
public function getOrdinal()
{
return $this->ordinal;
}
/**
* Returns the partition method
*
* @return string partition method
*/
public function getMethod()
{
return $this->method;
}
/**
* Returns the partition expression
*
* @return string partition expression
*/
public function getExpression()
{
return $this->expression;
}
/**
* Returns the number of data rows
*
* @return integer number of rows
*/
public function getRows()
{
return $this->rows;
}
/**
* Returns the data length
*
* @return integer data length
*/
public function getDataLength()
{
return $this->dataLength;
}
/**
* Returns the index length
*
* @return integer index length
*/
public function getIndexLength()
{
return $this->indexLength;
}
/**
* Returns the partition comment
*
* @return string partition comment
*/
public function getComment()
{
return $this->comment;
}
}

View File

@ -0,0 +1,48 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold PMA\libraries\SysInfo class
*
* @package PMA
*/
namespace PMA\libraries;
/**
* Basic sysinfo class not providing any real data.
*
* @package PhpMyAdmin-sysinfo
*/
class SysInfo
{
public $os = PHP_OS;
/**
* Gets load information
*
* @return array with load data
*/
public function loadavg()
{
return array('loadavg' => 0);
}
/**
* Gets information about memory usage
*
* @return array with memory usage data
*/
public function memory()
{
return array();
}
/**
* Checks whether class is supported in this environment
*
* @return true on success
*/
public function supported()
{
return true;
}
}

View File

@ -0,0 +1,91 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold PMA\libraries\SysInfoLinux class
*
* @package PMA
*/
namespace PMA\libraries;
/**
* Linux based SysInfo class
*
* @package PhpMyAdmin-sysinfo
*/
class SysInfoLinux extends \PMA\libraries\SysInfo
{
public $os = 'Linux';
/**
* Gets load information
*
* @return array with load data
*/
function loadavg()
{
$buf = file_get_contents('/proc/stat');
$nums = preg_split(
"/\s+/",
mb_substr(
$buf,
0,
mb_strpos($buf, "\n")
)
);
return Array(
'busy' => $nums[1] + $nums[2] + $nums[3],
'idle' => intval($nums[4]),
);
}
/**
* Checks whether class is supported in this environment
*
* @return true on success
*/
public function supported()
{
return @is_readable('/proc/meminfo') && @is_readable('/proc/stat');
}
/**
* Gets information about memory usage
*
* @return array with memory usage data
*/
function memory()
{
preg_match_all(
MEMORY_REGEXP,
file_get_contents('/proc/meminfo'),
$matches
);
$mem = array_combine($matches[1], $matches[2]);
$defaults = array(
'MemTotal' => 0,
'MemFree' => 0,
'Cached' => 0,
'Buffers' => 0,
'SwapTotal' => 0,
'SwapFree' => 0,
'SwapCached' => 0,
);
$mem = array_merge($defaults, $mem);
$mem['MemUsed'] = $mem['MemTotal']
- $mem['MemFree'] - $mem['Cached'] - $mem['Buffers'];
$mem['SwapUsed'] = $mem['SwapTotal']
- $mem['SwapFree'] - $mem['SwapCached'];
foreach ($mem as $idx => $value) {
$mem[$idx] = intval($value);
}
return $mem;
}
}

View File

@ -0,0 +1,80 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold PMA\libraries\SysInfoSunOS class
*
* @package PMA
*/
namespace PMA\libraries;
/**
* SunOS based SysInfo class
*
* @package PhpMyAdmin-sysinfo
*/
class SysInfoSunOS extends \PMA\libraries\SysInfo
{
public $os = 'SunOS';
/**
* Read value from kstat
*
* @param string $key Key to read
*
* @return string with value
*/
private function _kstat($key)
{
if ($m = shell_exec('kstat -p d ' . $key)) {
list(, $value) = preg_split("/\t/", trim($m), 2);
return $value;
} else {
return '';
}
}
/**
* Gets load information
*
* @return array with load data
*/
public function loadavg()
{
$load1 = $this->_kstat('unix:0:system_misc:avenrun_1min');
return array('loadavg' => $load1);
}
/**
* Checks whether class is supported in this environment
*
* @return true on success
*/
public function supported()
{
return @is_readable('/proc/meminfo');
}
/**
* Gets information about memory usage
*
* @return array with memory usage data
*/
public function memory()
{
$pagesize = $this->_kstat('unix:0:seg_cache:slab_size');
$mem = array();
$mem['MemTotal']
= $this->_kstat('unix:0:system_pages:pagestotal') * $pagesize;
$mem['MemUsed']
= $this->_kstat('unix:0:system_pages:pageslocked') * $pagesize;
$mem['MemFree']
= $this->_kstat('unix:0:system_pages:pagesfree') * $pagesize;
$mem['SwapTotal'] = $this->_kstat('unix:0:vminfo:swap_avail') / 1024;
$mem['SwapUsed'] = $this->_kstat('unix:0:vminfo:swap_alloc') / 1024;
$mem['SwapFree'] = $this->_kstat('unix:0:vminfo:swap_free') / 1024;
return $mem;
}
}

View File

@ -0,0 +1,126 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Hold PMA\libraries\SysInfoWINNT class
*
* @package PMA
*/
namespace PMA\libraries;
use COM;
/**
* Windows NT based SysInfo class
*
* @package PhpMyAdmin-sysinfo
*/
class SysInfoWINNT extends \PMA\libraries\SysInfo
{
private $_wmi;
public $os = 'WINNT';
/**
* Constructor to access to wmi database.
*/
public function __construct()
{
if (!class_exists('COM')) {
$this->_wmi = null;
} else {
// initialize the wmi object
$objLocator = new COM('WbemScripting.SWbemLocator');
$this->_wmi = $objLocator->ConnectServer();
}
}
/**
* Gets load information
*
* @return array with load data
*/
function loadavg()
{
$loadavg = "";
$sum = 0;
$buffer = $this->_getWMI('Win32_Processor', array('LoadPercentage'));
foreach ($buffer as $load) {
$value = $load['LoadPercentage'];
$loadavg .= $value . ' ';
$sum += $value;
}
return array('loadavg' => $sum / count($buffer));
}
/**
* Checks whether class is supported in this environment
*
* @return true on success
*/
public function supported()
{
return !is_null($this->_wmi);
}
/**
* Reads data from WMI
*
* @param string $strClass Class to read
* @param array $strValue Values to read
*
* @return array with results
*/
private function _getWMI($strClass, $strValue = array())
{
$arrData = array();
$objWEBM = $this->_wmi->Get($strClass);
$arrProp = $objWEBM->Properties_;
$arrWEBMCol = $objWEBM->Instances_();
foreach ($arrWEBMCol as $objItem) {
$arrInstance = array();
foreach ($arrProp as $propItem) {
$name = $propItem->Name;
if (empty($strValue) || in_array($name, $strValue)) {
$value = $objItem->$name;
$arrInstance[$name] = trim($value);
}
}
$arrData[] = $arrInstance;
}
return $arrData;
}
/**
* Gets information about memory usage
*
* @return array with memory usage data
*/
function memory()
{
$buffer = $this->_getWMI(
"Win32_OperatingSystem",
array('TotalVisibleMemorySize', 'FreePhysicalMemory')
);
$mem = Array();
$mem['MemTotal'] = $buffer[0]['TotalVisibleMemorySize'];
$mem['MemFree'] = $buffer[0]['FreePhysicalMemory'];
$mem['MemUsed'] = $mem['MemTotal'] - $mem['MemFree'];
$buffer = $this->_getWMI('Win32_PageFileUsage');
$mem['SwapTotal'] = 0;
$mem['SwapUsed'] = 0;
$mem['SwapPeak'] = 0;
foreach ($buffer as $swapdevice) {
$mem['SwapTotal'] += $swapdevice['AllocatedBaseSize'] * 1024;
$mem['SwapUsed'] += $swapdevice['CurrentUsage'] * 1024;
$mem['SwapPeak'] += $swapdevice['PeakUsage'] * 1024;
}
return $mem;
}
}

View File

@ -0,0 +1,125 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* hold PMA\libraries\SystemDatabase class
*
* @package PMA
*/
namespace PMA\libraries;
require_once 'libraries/database_interface.inc.php';
/**
* Class SystemDatabase
*
* @package PMA
*/
class SystemDatabase
{
/**
* @var DatabaseInterface
*/
protected $dbi;
/**
* Get instance of SystemDatabase
*
* @param DatabaseInterface $dbi Database interface for the system database
*
*/
function __construct(DatabaseInterface $dbi)
{
$this->dbi = $dbi;
}
/**
* Get existing data on transformations applied for
* columns in a particular table
*
* @param string $db Database name looking for
*
* @return \mysqli_result Result of executed SQL query
*/
public function getExistingTransformationData($db)
{
$cfgRelation = \PMA_getRelationsParam();
// Get the existing transformation details of the same database
// from pma__column_info table
$pma_transformation_sql = sprintf(
"SELECT * FROM %s.%s WHERE `db_name` = '%s'",
Util::backquote($cfgRelation['db']),
Util::backquote($cfgRelation['column_info']),
$GLOBALS['dbi']->escapeString($db)
);
return $this->dbi->tryQuery($pma_transformation_sql);
}
/**
* Get SQL query for store new transformation details of a VIEW
*
* @param object $pma_transformation_data Result set of SQL execution
* @param array $column_map Details of VIEW columns
* @param string $view_name Name of the VIEW
* @param string $db Database name of the VIEW
*
* @return string $new_transformations_sql SQL query for new transformations
*/
function getNewTransformationDataSql(
$pma_transformation_data, $column_map, $view_name, $db
) {
$cfgRelation = \PMA_getRelationsParam();
// Need to store new transformation details for VIEW
$new_transformations_sql = sprintf(
"INSERT INTO %s.%s ("
. "`db_name`, `table_name`, `column_name`, "
. "`comment`, `mimetype`, `transformation`, "
. "`transformation_options`) VALUES",
Util::backquote($cfgRelation['db']),
Util::backquote($cfgRelation['column_info'])
);
$column_count = 0;
$add_comma = false;
while ($data_row = $this->dbi->fetchAssoc($pma_transformation_data)) {
foreach ($column_map as $column) {
if ($data_row['table_name'] != $column['table_name']
|| $data_row['column_name'] != $column['refering_column']
) {
continue;
}
$new_transformations_sql .= sprintf(
"%s ('%s', '%s', '%s', '%s', '%s', '%s', '%s')",
$add_comma ? ', ' : '',
$db,
$view_name,
isset($column['real_column'])
? $column['real_column']
: $column['refering_column'],
$data_row['comment'],
$data_row['mimetype'],
$data_row['transformation'],
$GLOBALS['dbi']->escapeString(
$data_row['transformation_options']
)
);
$add_comma = true;
$column_count++;
break;
}
if ($column_count == count($column_map)) {
break;
}
}
return ($column_count > 0) ? $new_transformations_sql : '';
}
}

2535
#pma/libraries/Table.php Normal file

File diff suppressed because it is too large Load Diff

167
#pma/libraries/Template.php Normal file
View File

@ -0,0 +1,167 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* hold PMA\libraries\Template class
*
* @package PMA\libraries
*/
namespace PMA\libraries;
/**
* Class Template
*
* Handle front end templating
*
* @package PMA\libraries
*/
class Template
{
/**
* Name of the template
*/
protected $name = null;
/**
* Data associated with the template
*/
protected $data;
/**
* Helper functions for the template
*/
protected $helperFunctions;
const BASE_PATH = 'templates/';
/**
* Template constructor
*
* @param string $name Template name
* @param array $data Variables to be provided to the template
* @param array $helperFunctions Helper functions to be used by template
*/
protected function __construct($name, $data = array(), $helperFunctions = array())
{
$this->name = $name;
$this->data = $data;
$this->helperFunctions = $helperFunctions;
}
/**
* Template getter
*
* @param string $name Template name
* @param array $data Variables to be provided to the template
* @param array $helperFunctions Helper functions to be used by template
*
* @return Template
*/
public static function get($name, $data = array(), $helperFunctions = array())
{
return new Template($name, $data, $helperFunctions);
}
/**
* Adds more entries to the data for this template
*
* @param array|string $data containing data array or data key
* @param string $value containing data value
*/
public function set($data, $value = null)
{
if(is_array($data) && ! $value) {
$this->data = array_merge(
$this->data,
$data
);
} else if (is_string($data)) {
$this->data[$data] = $value;
}
}
/**
* Adds a function for use by the template
*
* @param string $funcName function name
* @param callable $funcDef function definition
*/
public function setHelper($funcName, $funcDef)
{
if (! isset($this->helperFunctions[$funcName])) {
$this->helperFunctions[$funcName] = $funcDef;
} else {
throw new \LogicException(
'The function "' . $funcName . '" is already associated with the template.'
);
}
}
/**
* Removes a function
*
* @param string $funcName function name
*/
public function removeHelper($funcName)
{
if (isset($this->helperFunctions[$funcName])) {
unset($this->helperFunctions[$funcName]);
} else {
throw new \LogicException(
'The function "' . $funcName . '" is not associated with the template.'
);
}
}
/**
* Magic call to locally inaccessible but associated helper functions
*
* @param string $funcName function name
* @param array $arguments function arguments
*/
public function __call($funcName, $arguments)
{
if (isset($this->helperFunctions[$funcName])) {
return call_user_func_array($this->helperFunctions[$funcName], $arguments);
} else {
throw new \LogicException(
'The function "' . $funcName . '" is not associated with the template.'
);
}
}
/**
* Render template
*
* @param array $data Variables to be provided to the template
* @param bool $trim Trim content (does nothing, removed in future)
* @param array $helperFunctions Helper functions to be used by template
*
* @return string
*/
public function render($data = array(), $trim = true, $helperFunctions = array())
{
$template = static::BASE_PATH . $this->name . '.phtml';
try {
$this->set($data);
$this->helperFunctions = array_merge(
$this->helperFunctions,
$helperFunctions
);
extract($this->data);
ob_start();
if (file_exists($template)) {
include $template;
} else {
throw new \LogicException(
'The template "' . $template . '" not found.'
);
}
$content = ob_get_clean();
return $content;
} catch (\LogicException $e) {
ob_end_clean();
throw new \LogicException($e->getMessage());
}
}
}

482
#pma/libraries/Theme.php Normal file
View File

@ -0,0 +1,482 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* hold Theme class
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* handles theme
*
* @todo add the possibility to make a theme depend on another theme
* and by default on original
* @todo make all components optional - get missing components from 'parent' theme
*
* @package PhpMyAdmin
*/
class Theme
{
/**
* @var string theme version
* @access protected
*/
var $version = '0.0.0.0';
/**
* @var string theme name
* @access protected
*/
var $name = '';
/**
* @var string theme id
* @access protected
*/
var $id = '';
/**
* @var string theme path
* @access protected
*/
var $path = '';
/**
* @var string image path
* @access protected
*/
var $img_path = '';
/**
* @var integer last modification time for info file
* @access protected
*/
var $mtime_info = 0;
/**
* needed because sometimes, the mtime for different themes
* is identical
* @var integer filesize for info file
* @access protected
*/
var $filesize_info = 0;
/**
* @var array List of css files to load
* @access private
*/
private $_cssFiles = array(
'common',
'enum_editor',
'gis',
'navigation',
'pmd',
'rte',
'codemirror',
'jqplot',
'resizable-menu'
);
/**
* Loads theme information
*
* @return boolean whether loading them info was successful or not
* @access public
*/
function loadInfo()
{
if (! file_exists($this->getPath() . '/info.inc.php')) {
return false;
}
if ($this->mtime_info === filemtime($this->getPath() . '/info.inc.php')) {
return true;
}
@include $this->getPath() . '/info.inc.php';
// was it set correctly?
if (! isset($theme_name)) {
return false;
}
$this->mtime_info = filemtime($this->getPath() . '/info.inc.php');
$this->filesize_info = filesize($this->getPath() . '/info.inc.php');
if (isset($theme_full_version)) {
$this->setVersion($theme_full_version);
} elseif (isset($theme_generation, $theme_version)) {
$this->setVersion($theme_generation . '.' . $theme_version);
}
$this->setName($theme_name);
return true;
}
/**
* returns theme object loaded from given folder
* or false if theme is invalid
*
* @param string $folder path to theme
*
* @return Theme|false
* @static
* @access public
*/
static public function load($folder)
{
$theme = new Theme();
$theme->setPath($folder);
if (! $theme->loadInfo()) {
return false;
}
$theme->checkImgPath();
return $theme;
}
/**
* checks image path for existence - if not found use img from fallback theme
*
* @access public
* @return bool
*/
public function checkImgPath()
{
// try current theme first
if (is_dir($this->getPath() . '/img/')) {
$this->setImgPath($this->getPath() . '/img/');
return true;
}
// try fallback theme
$fallback = $GLOBALS['cfg']['ThemePath'] . '/'
. ThemeManager::FALLBACK_THEME
. '/img/';
if (is_dir($fallback)) {
$this->setImgPath($fallback);
return true;
}
// we failed
trigger_error(
sprintf(
__('No valid image path for theme %s found!'),
$this->getName()
),
E_USER_ERROR
);
return false;
}
/**
* returns path to theme
*
* @access public
* @return string path to theme
*/
public function getPath()
{
return $this->path;
}
/**
* returns layout file
*
* @access public
* @return string layout file
*/
public function getLayoutFile()
{
return $this->getPath() . '/layout.inc.php';
}
/**
* set path to theme
*
* @param string $path path to theme
*
* @return void
* @access public
*/
public function setPath($path)
{
$this->path = trim($path);
}
/**
* sets version
*
* @param string $version version to set
*
* @return void
* @access public
*/
public function setVersion($version)
{
$this->version = trim($version);
}
/**
* returns version
*
* @return string version
* @access public
*/
public function getVersion()
{
return $this->version;
}
/**
* checks theme version against $version
* returns true if theme version is equal or higher to $version
*
* @param string $version version to compare to
*
* @return boolean true if theme version is equal or higher to $version
* @access public
*/
public function checkVersion($version)
{
return version_compare($this->getVersion(), $version, 'lt');
}
/**
* sets name
*
* @param string $name name to set
*
* @return void
* @access public
*/
public function setName($name)
{
$this->name = trim($name);
}
/**
* returns name
*
* @access public
* @return string name
*/
public function getName()
{
return $this->name;
}
/**
* sets id
*
* @param string $id new id
*
* @return void
* @access public
*/
public function setId($id)
{
$this->id = trim($id);
}
/**
* returns id
*
* @return string id
* @access public
*/
public function getId()
{
return $this->id;
}
/**
* Sets path to images for the theme
*
* @param string $path path to images for this theme
*
* @return void
* @access public
*/
public function setImgPath($path)
{
$this->img_path = $path;
}
/**
* Returns the path to image for the theme.
* If filename is given, it possibly fallbacks to fallback
* theme for it if image does not exist.
*
* @param string $file file name for image
*
* @access public
* @return string image path for this theme
*/
public function getImgPath($file = null)
{
if (is_null($file)) {
return $this->img_path;
}
if (is_readable($this->img_path . $file)) {
return $this->img_path . $file;
}
return $GLOBALS['cfg']['ThemePath'] . '/'
. ThemeManager::FALLBACK_THEME . '/img/' . $file;
}
/**
* load css (send to stdout, normally the browser)
*
* @return bool
* @access public
*/
public function loadCss()
{
$success = true;
if ($GLOBALS['text_dir'] === 'ltr') {
$right = 'right';
$left = 'left';
} else {
$right = 'left';
$left = 'right';
}
foreach ($this->_cssFiles as $file) {
$path = $this->getPath() . "/css/$file.css.php";
$fallback = "./themes/"
. ThemeManager::FALLBACK_THEME . "/css/$file.css.php";
if (is_readable($path)) {
echo "\n/* FILE: " , $file , ".css.php */\n";
include $path;
} else if (is_readable($fallback)) {
echo "\n/* FILE: " , $file , ".css.php */\n";
include $fallback;
} else {
$success = false;
}
}
include './themes/sprites.css.php';
return $success;
}
/**
* Renders the preview for this theme
*
* @return string
* @access public
*/
public function getPrintPreview()
{
$url_params = array('set_theme' => $this->getId());
$url = 'index.php' . PMA_URL_getCommon($url_params);
$retval = '<div class="theme_preview">';
$retval .= '<h2>';
$retval .= htmlspecialchars($this->getName());
$retval .= ' (' . htmlspecialchars($this->getVersion()) . ') ';
$retval .= '</h2>';
$retval .= '<p>';
$retval .= '<a class="take_theme" ';
$retval .= 'name="' . htmlspecialchars($this->getId()) . '" ';
$retval .= 'href="' . $url . '">';
if (@file_exists($this->getPath() . '/screen.png')) {
// if screen exists then output
$retval .= '<img src="' . $this->getPath() . '/screen.png" border="1"';
$retval .= ' alt="' . htmlspecialchars($this->getName()) . '"';
$retval .= ' title="' . htmlspecialchars($this->getName()) . '" />';
$retval .= '<br />';
} else {
$retval .= __('No preview available.');
}
$retval .= '[ <strong>' . __('take it') . '</strong> ]';
$retval .= '</a>';
$retval .= '</p>';
$retval .= '</div>';
return $retval;
}
/**
* Remove filter for IE.
*
* @return string CSS code.
*/
function getCssIEClearFilter()
{
return PMA_USR_BROWSER_AGENT == 'IE'
&& PMA_USR_BROWSER_VER >= 6
&& PMA_USR_BROWSER_VER <= 8
? 'filter: none'
: '';
}
/**
* Gets currently configured font size.
*
* @return String with font size.
*/
function getFontSize()
{
$fs = $GLOBALS['PMA_Config']->get('fontsize');
if (!is_null($fs)) {
return $fs;
}
if (isset($_COOKIE['pma_fontsize'])) {
return htmlspecialchars($_COOKIE['pma_fontsize']);
}
return '82%';
}
/**
* Generates code for CSS gradient using various browser extensions.
*
* @param string $start_color Color of gradient start, hex value without #
* @param string $end_color Color of gradient end, hex value without #
*
* @return string CSS code.
*/
function getCssGradient($start_color, $end_color)
{
$result = array();
// Opera 9.5+, IE 9
$result[] = 'background-image: url(./themes/svg_gradient.php?from='
. $start_color . '&to=' . $end_color . ');';
$result[] = 'background-size: 100% 100%;';
// Safari 4-5, Chrome 1-9
$result[] = 'background: '
. '-webkit-gradient(linear, left top, left bottom, from(#'
. $start_color . '), to(#' . $end_color . '));';
// Safari 5.1, Chrome 10+
$result[] = 'background: -webkit-linear-gradient(top, #'
. $start_color . ', #' . $end_color . ');';
// Firefox 3.6+
$result[] = 'background: -moz-linear-gradient(top, #'
. $start_color . ', #' . $end_color . ');';
// IE 10
$result[] = 'background: -ms-linear-gradient(top, #'
. $start_color . ', #' . $end_color . ');';
// Opera 11.10
$result[] = 'background: -o-linear-gradient(top, #'
. $start_color . ', #' . $end_color . ');';
// IE 6-8
if (PMA_USR_BROWSER_AGENT == 'IE'
&& PMA_USR_BROWSER_VER >= 6
&& PMA_USR_BROWSER_VER <= 8
) {
$result[] = 'filter: '
. 'progid:DXImageTransform.Microsoft.gradient(startColorstr="#'
. $start_color . '", endColorstr="#' . $end_color . '");';
}
return implode("\n", $result);
}
}

View File

@ -0,0 +1,526 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* phpMyAdmin theme manager
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* phpMyAdmin theme manager
*
* @package PhpMyAdmin
*/
class ThemeManager
{
/**
* @var string path to theme folder
* @access protected
*/
private $_themes_path;
/**
* @var array available themes
*/
var $themes = array();
/**
* @var string cookie name
*/
var $cookie_name = 'pma_theme';
/**
* @var boolean
*/
var $per_server = false;
/**
* @var string name of active theme
*/
var $active_theme = '';
/**
* @var Theme Theme active theme
*/
var $theme = null;
/**
* @var string
*/
var $theme_default;
/**
* @const string The name of the fallback theme
*/
const FALLBACK_THEME = 'pmahomme';
/**
* Constructor for Theme Manager class
*
* @access public
*/
public function __construct()
{
$this->init();
}
/**
* sets path to folder containing the themes
*
* @param string $path path to themes folder
*
* @access public
* @return boolean success
*/
public function setThemesPath($path)
{
if (! $this->_checkThemeFolder($path)) {
return false;
}
$this->_themes_path = trim($path);
return true;
}
/**
* Returns path to folder containing themes
*
* @access public
* @return string theme path
*/
public function getThemesPath()
{
return $this->_themes_path;
}
/**
* sets if there are different themes per server
*
* @param boolean $per_server Whether to enable per server flag
*
* @access public
* @return void
*/
public function setThemePerServer($per_server)
{
$this->per_server = (bool) $per_server;
}
/**
* Initialise the class
*
* @access public
* @return void
*/
public function init()
{
$this->themes = array();
$this->theme_default = self::FALLBACK_THEME;
$this->active_theme = '';
if (! $this->setThemesPath($GLOBALS['cfg']['ThemePath'])) {
return;
}
$this->setThemePerServer($GLOBALS['cfg']['ThemePerServer']);
$this->loadThemes();
$this->theme = new Theme;
if (! $this->checkTheme($GLOBALS['cfg']['ThemeDefault'])) {
trigger_error(
sprintf(
__('Default theme %s not found!'),
htmlspecialchars($GLOBALS['cfg']['ThemeDefault'])
),
E_USER_ERROR
);
$GLOBALS['cfg']['ThemeDefault'] = false;
}
$this->theme_default = $GLOBALS['cfg']['ThemeDefault'];
// check if user have a theme cookie
if (! $this->getThemeCookie()
|| ! $this->setActiveTheme($this->getThemeCookie())
) {
if ($GLOBALS['cfg']['ThemeDefault']) {
// otherwise use default theme
$this->setActiveTheme($this->theme_default);
} else {
// or fallback theme
$this->setActiveTheme(self::FALLBACK_THEME);
}
}
}
/**
* Checks configuration
*
* @access public
* @return void
*/
public function checkConfig()
{
if ($this->_themes_path != trim($GLOBALS['cfg']['ThemePath'])
|| $this->theme_default != $GLOBALS['cfg']['ThemeDefault']
) {
$this->init();
} else {
// at least the theme path needs to be checked every time for new
// themes, as there is no other way at the moment to keep track of
// new or removed themes
$this->loadThemes();
}
}
/**
* Sets active theme
*
* @param string $theme theme name
*
* @access public
* @return bool true on success
*/
public function setActiveTheme($theme = null)
{
if (! $this->checkTheme($theme)) {
trigger_error(
sprintf(
__('Theme %s not found!'),
htmlspecialchars($theme)
),
E_USER_ERROR
);
return false;
}
$this->active_theme = $theme;
$this->theme = $this->themes[$theme];
// need to set later
//$this->setThemeCookie();
return true;
}
/**
* Returns name for storing theme
*
* @return string cookie name
* @access public
*/
public function getThemeCookieName()
{
// Allow different theme per server
if (isset($GLOBALS['server']) && $this->per_server) {
return $this->cookie_name . '-' . $GLOBALS['server'];
} else {
return $this->cookie_name;
}
}
/**
* returns name of theme stored in the cookie
*
* @return string theme name from cookie
* @access public
*/
public function getThemeCookie()
{
if (isset($_COOKIE[$this->getThemeCookieName()])) {
return $_COOKIE[$this->getThemeCookieName()];
}
return false;
}
/**
* save theme in cookie
*
* @return bool true
* @access public
*/
public function setThemeCookie()
{
$GLOBALS['PMA_Config']->setCookie(
$this->getThemeCookieName(),
$this->theme->id,
$this->theme_default
);
// force a change of a dummy session variable to avoid problems
// with the caching of phpmyadmin.css.php
$GLOBALS['PMA_Config']->set('theme-update', $this->theme->id);
return true;
}
/**
* Checks whether folder is valid for storing themes
*
* @param string $folder Folder name to test
*
* @return boolean
* @access private
*/
private function _checkThemeFolder($folder)
{
if (! is_dir($folder)) {
trigger_error(
sprintf(
__('Theme path not found for theme %s!'),
htmlspecialchars($folder)
),
E_USER_ERROR
);
return false;
}
return true;
}
/**
* read all themes
*
* @return bool true
* @access public
*/
public function loadThemes()
{
$this->themes = array();
if (false === ($handleThemes = opendir($this->getThemesPath()))) {
trigger_error(
'phpMyAdmin-ERROR: cannot open themes folder: '
. $this->getThemesPath(),
E_USER_WARNING
);
return false;
}
// check for themes directory
while (false !== ($PMA_Theme = readdir($handleThemes))) {
// Skip non dirs, . and ..
if ($PMA_Theme == '.'
|| $PMA_Theme == '..'
|| ! is_dir($this->getThemesPath() . '/' . $PMA_Theme)
) {
continue;
}
if (array_key_exists($PMA_Theme, $this->themes)) {
continue;
}
$new_theme = Theme::load(
$this->getThemesPath() . '/' . $PMA_Theme
);
if ($new_theme) {
$new_theme->setId($PMA_Theme);
$this->themes[$PMA_Theme] = $new_theme;
}
} // end get themes
closedir($handleThemes);
ksort($this->themes);
return true;
}
/**
* checks if given theme name is a known theme
*
* @param string $theme name fo theme to check for
*
* @return bool
* @access public
*/
public function checkTheme($theme)
{
if (! array_key_exists($theme, $this->themes)) {
return false;
}
return true;
}
/**
* returns HTML selectbox, with or without form enclosed
*
* @param boolean $form whether enclosed by from tags or not
*
* @return string
* @access public
*/
public function getHtmlSelectBox($form = true)
{
$select_box = '';
if ($form) {
$select_box .= '<form name="setTheme" method="get"';
$select_box .= ' action="index.php" class="disableAjax">';
$select_box .= PMA_URL_getHiddenInputs();
}
$theme_preview_path= './themes.php';
$theme_preview_href = '<a href="'
. $theme_preview_path . '" target="themes" class="themeselect">';
$select_box .= $theme_preview_href . __('Theme:') . '</a>' . "\n";
$select_box .= '<select name="set_theme" lang="en" dir="ltr"'
. ' class="autosubmit">';
foreach ($this->themes as $each_theme_id => $each_theme) {
$select_box .= '<option value="' . $each_theme_id . '"';
if ($this->active_theme === $each_theme_id) {
$select_box .= ' selected="selected"';
}
$select_box .= '>' . htmlspecialchars($each_theme->getName())
. '</option>';
}
$select_box .= '</select>';
if ($form) {
$select_box .= '</form>';
}
return $select_box;
}
/**
* enables backward compatibility
*
* @return void
* @access public
*/
public function makeBc()
{
$GLOBALS['theme'] = $this->theme->getId();
$GLOBALS['pmaThemePath'] = $this->theme->getPath();
$GLOBALS['pmaThemeImage'] = $this->theme->getImgPath();
/**
* load layout file if exists
*/
if (file_exists($this->theme->getLayoutFile())) {
include $this->theme->getLayoutFile();
}
}
/**
* Renders the previews for all themes
*
* @return string
* @access public
*/
public function getPrintPreviews()
{
$retval = '';
foreach ($this->themes as $each_theme) {
$retval .= $each_theme->getPrintPreview();
} // end 'open themes'
return $retval;
}
/**
* returns Theme object for fall back theme
*
* @return Theme fall back theme
* @access public
*/
public function getFallBackTheme()
{
if (isset($this->themes[self::FALLBACK_THEME])) {
return $this->themes[self::FALLBACK_THEME];
}
return false;
}
/**
* prints css data
*
* @return bool
* @access public
*/
public function printCss()
{
if ($this->theme->loadCss()) {
return true;
}
// if loading css for this theme failed, try default theme css
$fallback_theme = $this->getFallBackTheme();
if ($fallback_theme && $fallback_theme->loadCss()) {
return true;
}
return false;
}
/**
* Theme initialization
*
* @return void
* @access public
*/
public static function initializeTheme()
{
/**
* @global ThemeManager $_SESSION['ThemeManager']
*/
if (! isset($_SESSION['PMA_Theme_Manager'])) {
$_SESSION['PMA_Theme_Manager'] = new ThemeManager;
} else {
/**
* @todo move all __wakeup() functionality into session.inc.php
*/
$_SESSION['PMA_Theme_Manager']->checkConfig();
}
// for the theme per server feature
if (isset($_REQUEST['server']) && ! isset($_REQUEST['set_theme'])) {
$GLOBALS['server'] = $_REQUEST['server'];
$tmp = $_SESSION['PMA_Theme_Manager']->getThemeCookie();
if (empty($tmp)) {
$tmp = $_SESSION['PMA_Theme_Manager']->theme_default;
}
$_SESSION['PMA_Theme_Manager']->setActiveTheme($tmp);
}
/**
* @todo move into ThemeManager::__wakeup()
*/
if (isset($_REQUEST['set_theme'])) {
// if user selected a theme
$_SESSION['PMA_Theme_Manager']->setActiveTheme($_REQUEST['set_theme']);
}
/**
* the theme object
*
*@global Theme $_SESSION['PMA_Theme']
*/
$_SESSION['PMA_Theme'] = $_SESSION['PMA_Theme_Manager']->theme;
// BC
/**
* the active theme
* @global string $GLOBALS['theme']
*/
$GLOBALS['theme'] = $_SESSION['PMA_Theme']->getName();
/**
* the theme path
* @global string $GLOBALS['pmaThemePath']
*/
$GLOBALS['pmaThemePath'] = $_SESSION['PMA_Theme']->getPath();
/**
* the theme image path
* @global string $GLOBALS['pmaThemeImage']
*/
$GLOBALS['pmaThemeImage'] = $_SESSION['PMA_Theme']->getImgPath();
/**
* load layout file if exists
*/
if (@file_exists($_SESSION['PMA_Theme']->getLayoutFile())) {
include $_SESSION['PMA_Theme']->getLayoutFile();
}
}
}

959
#pma/libraries/Tracker.php Normal file
View File

@ -0,0 +1,959 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Tracking changes on databases, tables and views
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use PMA\libraries\plugins\export\ExportSql;
/**
* This class tracks changes on databases, tables and views.
*
* @package PhpMyAdmin
*
* @todo use stristr instead of strstr
*/
class Tracker
{
/**
* Whether tracking is ready.
*/
static protected $enabled = false;
/**
* Actually enables tracking. This needs to be done after all
* underlaying code is initialized.
*
* @static
*
* @return void
*/
static public function enable()
{
self::$enabled = true;
}
/**
* Gets the on/off value of the Tracker module, starts initialization.
*
* @static
*
* @return boolean (true=on|false=off)
*/
static public function isActive()
{
if (! self::$enabled) {
return false;
}
/* We need to avoid attempt to track any queries
* from PMA_getRelationsParam
*/
self::$enabled = false;
$cfgRelation = PMA_getRelationsParam();
/* Restore original state */
self::$enabled = true;
if (! $cfgRelation['trackingwork']) {
return false;
}
$pma_table = self::_getTrackingTable();
if (isset($pma_table)) {
return true;
} else {
return false;
}
}
/**
* Parses the name of a table from a SQL statement substring.
*
* @param string $string part of SQL statement
*
* @static
*
* @return string the name of table
*/
static protected function getTableName($string)
{
if (mb_strstr($string, '.')) {
$temp = explode('.', $string);
$tablename = $temp[1];
} else {
$tablename = $string;
}
$str = explode("\n", $tablename);
$tablename = $str[0];
$tablename = str_replace(';', '', $tablename);
$tablename = str_replace('`', '', $tablename);
$tablename = trim($tablename);
return $tablename;
}
/**
* Gets the tracking status of a table, is it active or deactive ?
*
* @param string $dbname name of database
* @param string $tablename name of table
*
* @static
*
* @return boolean true or false
*/
static public function isTracked($dbname, $tablename)
{
if (! self::$enabled) {
return false;
}
/* We need to avoid attempt to track any queries
* from PMA_getRelationsParam
*/
self::$enabled = false;
$cfgRelation = PMA_getRelationsParam();
/* Restore original state */
self::$enabled = true;
if (! $cfgRelation['trackingwork']) {
return false;
}
$sql_query = " SELECT tracking_active FROM " . self::_getTrackingTable() .
" WHERE db_name = '" . $GLOBALS['dbi']->escapeString($dbname) . "' " .
" AND table_name = '" . $GLOBALS['dbi']->escapeString($tablename) . "' " .
" ORDER BY version DESC";
$row = $GLOBALS['dbi']->fetchArray(PMA_queryAsControlUser($sql_query));
if (isset($row['tracking_active']) && $row['tracking_active'] == 1) {
return true;
} else {
return false;
}
}
/**
* Returns the comment line for the log.
*
* @return string Comment, contains date and username
*/
static public function getLogComment()
{
$date = date('Y-m-d H:i:s');
$user = preg_replace('/\s+/', ' ', $GLOBALS['cfg']['Server']['user']);
return "# log " . $date . " " . $user . "\n";
}
/**
* Creates tracking version of a table / view
* (in other words: create a job to track future changes on the table).
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version
* @param string $tracking_set set of tracking statements
* @param bool $is_view if table is a view
*
* @static
*
* @return int result of version insertion
*/
static public function createVersion($dbname, $tablename, $version,
$tracking_set = '', $is_view = false
) {
global $sql_backquotes, $export_type;
if ($tracking_set == '') {
$tracking_set
= $GLOBALS['cfg']['Server']['tracking_default_statements'];
}
// get Export SQL instance
include_once "libraries/plugin_interface.lib.php";
/* @var $export_sql_plugin \PMA\libraries\plugins\export\ExportSql */
$export_sql_plugin = PMA_getPlugin(
"export",
"sql",
'libraries/plugins/export/',
array(
'export_type' => $export_type,
'single_table' => false,
)
);
$sql_backquotes = true;
$date = date('Y-m-d H:i:s');
// Get data definition snapshot of table
$columns = $GLOBALS['dbi']->getColumns($dbname, $tablename, null, true);
// int indices to reduce size
$columns = array_values($columns);
// remove Privileges to reduce size
for ($i = 0, $nb = count($columns); $i < $nb; $i++) {
unset($columns[$i]['Privileges']);
}
$indexes = $GLOBALS['dbi']->getTableIndexes($dbname, $tablename);
$snapshot = array('COLUMNS' => $columns, 'INDEXES' => $indexes);
$snapshot = serialize($snapshot);
// Get DROP TABLE / DROP VIEW and CREATE TABLE SQL statements
$sql_backquotes = true;
$create_sql = "";
if ($GLOBALS['cfg']['Server']['tracking_add_drop_table'] == true
&& $is_view == false
) {
$create_sql .= self::getLogComment()
. 'DROP TABLE IF EXISTS ' . Util::backquote($tablename) . ";\n";
}
if ($GLOBALS['cfg']['Server']['tracking_add_drop_view'] == true
&& $is_view == true
) {
$create_sql .= self::getLogComment()
. 'DROP VIEW IF EXISTS ' . Util::backquote($tablename) . ";\n";
}
$create_sql .= self::getLogComment() .
$export_sql_plugin->getTableDef($dbname, $tablename, "\n", "");
// Save version
$sql_query = "/*NOTRACK*/\n" .
"INSERT INTO " . self::_getTrackingTable() . " (" .
"db_name, " .
"table_name, " .
"version, " .
"date_created, " .
"date_updated, " .
"schema_snapshot, " .
"schema_sql, " .
"data_sql, " .
"tracking " .
") " .
"values (
'" . $GLOBALS['dbi']->escapeString($dbname) . "',
'" . $GLOBALS['dbi']->escapeString($tablename) . "',
'" . $GLOBALS['dbi']->escapeString($version) . "',
'" . $GLOBALS['dbi']->escapeString($date) . "',
'" . $GLOBALS['dbi']->escapeString($date) . "',
'" . $GLOBALS['dbi']->escapeString($snapshot) . "',
'" . $GLOBALS['dbi']->escapeString($create_sql) . "',
'" . $GLOBALS['dbi']->escapeString("\n") . "',
'" . $GLOBALS['dbi']->escapeString($tracking_set)
. "' )";
$result = PMA_queryAsControlUser($sql_query);
if ($result) {
// Deactivate previous version
self::deactivateTracking($dbname, $tablename, ($version - 1));
}
return $result;
}
/**
* Removes all tracking data for a table or a version of a table
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version
*
* @static
*
* @return int result of version insertion
*/
static public function deleteTracking($dbname, $tablename, $version = '')
{
$sql_query = "/*NOTRACK*/\n"
. "DELETE FROM " . self::_getTrackingTable()
. " WHERE `db_name` = '"
. $GLOBALS['dbi']->escapeString($dbname) . "'"
. " AND `table_name` = '"
. $GLOBALS['dbi']->escapeString($tablename) . "'";
if ($version) {
$sql_query .= " AND `version` = '"
. $GLOBALS['dbi']->escapeString($version) . "'";
}
$result = PMA_queryAsControlUser($sql_query);
return $result;
}
/**
* Creates tracking version of a database
* (in other words: create a job to track future changes on the database).
*
* @param string $dbname name of database
* @param string $version version
* @param string $query query
* @param string $tracking_set set of tracking statements
*
* @static
*
* @return int result of version insertion
*/
static public function createDatabaseVersion($dbname, $version, $query,
$tracking_set = 'CREATE DATABASE,ALTER DATABASE,DROP DATABASE'
) {
$date = date('Y-m-d H:i:s');
if ($tracking_set == '') {
$tracking_set
= $GLOBALS['cfg']['Server']['tracking_default_statements'];
}
$create_sql = "";
if ($GLOBALS['cfg']['Server']['tracking_add_drop_database'] == true) {
$create_sql .= self::getLogComment()
. 'DROP DATABASE IF EXISTS ' . Util::backquote($dbname) . ";\n";
}
$create_sql .= self::getLogComment() . $query;
// Save version
$sql_query = "/*NOTRACK*/\n" .
"INSERT INTO " . self::_getTrackingTable() . " (" .
"db_name, " .
"table_name, " .
"version, " .
"date_created, " .
"date_updated, " .
"schema_snapshot, " .
"schema_sql, " .
"data_sql, " .
"tracking " .
") " .
"values (
'" . $GLOBALS['dbi']->escapeString($dbname) . "',
'" . $GLOBALS['dbi']->escapeString('') . "',
'" . $GLOBALS['dbi']->escapeString($version) . "',
'" . $GLOBALS['dbi']->escapeString($date) . "',
'" . $GLOBALS['dbi']->escapeString($date) . "',
'" . $GLOBALS['dbi']->escapeString('') . "',
'" . $GLOBALS['dbi']->escapeString($create_sql) . "',
'" . $GLOBALS['dbi']->escapeString("\n") . "',
'" . $GLOBALS['dbi']->escapeString($tracking_set)
. "' )";
$result = PMA_queryAsControlUser($sql_query);
return $result;
}
/**
* Changes tracking of a table.
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version
* @param integer $new_state the new state of tracking
*
* @static
*
* @return int result of SQL query
*/
static private function _changeTracking($dbname, $tablename,
$version, $new_state
) {
$sql_query = " UPDATE " . self::_getTrackingTable() .
" SET `tracking_active` = '" . $new_state . "' " .
" WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($dbname) . "' " .
" AND `table_name` = '" . $GLOBALS['dbi']->escapeString($tablename) . "' " .
" AND `version` = '" . $GLOBALS['dbi']->escapeString($version) . "' ";
$result = PMA_queryAsControlUser($sql_query);
return $result;
}
/**
* Changes tracking data of a table.
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version
* @param string $type type of data(DDL || DML)
* @param string|array $new_data the new tracking data
*
* @static
*
* @return bool result of change
*/
static public function changeTrackingData($dbname, $tablename,
$version, $type, $new_data
) {
if ($type == 'DDL') {
$save_to = 'schema_sql';
} elseif ($type == 'DML') {
$save_to = 'data_sql';
} else {
return false;
}
$date = date('Y-m-d H:i:s');
$new_data_processed = '';
if (is_array($new_data)) {
foreach ($new_data as $data) {
$new_data_processed .= '# log ' . $date . ' ' . $data['username']
. $GLOBALS['dbi']->escapeString($data['statement']) . "\n";
}
} else {
$new_data_processed = $new_data;
}
$sql_query = " UPDATE " . self::_getTrackingTable() .
" SET `" . $save_to . "` = '" . $new_data_processed . "' " .
" WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($dbname) . "' " .
" AND `table_name` = '" . $GLOBALS['dbi']->escapeString($tablename) . "' " .
" AND `version` = '" . $GLOBALS['dbi']->escapeString($version) . "' ";
$result = PMA_queryAsControlUser($sql_query);
return (boolean) $result;
}
/**
* Activates tracking of a table.
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version
*
* @static
*
* @return int result of SQL query
*/
static public function activateTracking($dbname, $tablename, $version)
{
return self::_changeTracking($dbname, $tablename, $version, 1);
}
/**
* Deactivates tracking of a table.
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version
*
* @static
*
* @return int result of SQL query
*/
static public function deactivateTracking($dbname, $tablename, $version)
{
return self::_changeTracking($dbname, $tablename, $version, 0);
}
/**
* Gets the newest version of a tracking job
* (in other words: gets the HEAD version).
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $statement tracked statement
*
* @static
*
* @return int (-1 if no version exists | > 0 if a version exists)
*/
static public function getVersion($dbname, $tablename, $statement = null)
{
$sql_query = " SELECT MAX(version) FROM " . self::_getTrackingTable() .
" WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($dbname) . "' " .
" AND `table_name` = '" . $GLOBALS['dbi']->escapeString($tablename) . "' ";
if ($statement != "") {
$sql_query .= " AND FIND_IN_SET('"
. $statement . "',tracking) > 0" ;
}
$row = $GLOBALS['dbi']->fetchArray(PMA_queryAsControlUser($sql_query));
return isset($row[0])
? $row[0]
: -1;
}
/**
* Gets the record of a tracking job.
*
* @param string $dbname name of database
* @param string $tablename name of table
* @param string $version version number
*
* @static
*
* @return mixed record DDM log, DDL log, structure snapshot, tracked
* statements.
*/
static public function getTrackedData($dbname, $tablename, $version)
{
$sql_query = " SELECT * FROM " . self::_getTrackingTable() .
" WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($dbname) . "' ";
if (! empty($tablename)) {
$sql_query .= " AND `table_name` = '"
. $GLOBALS['dbi']->escapeString($tablename) . "' ";
}
$sql_query .= " AND `version` = '" . $GLOBALS['dbi']->escapeString($version)
. "' " . " ORDER BY `version` DESC LIMIT 1";
$mixed = $GLOBALS['dbi']->fetchAssoc(PMA_queryAsControlUser($sql_query));
// Parse log
$log_schema_entries = explode('# log ', $mixed['schema_sql']);
$log_data_entries = explode('# log ', $mixed['data_sql']);
$ddl_date_from = $date = date('Y-m-d H:i:s');
$ddlog = array();
$first_iteration = true;
// Iterate tracked data definition statements
// For each log entry we want to get date, username and statement
foreach ($log_schema_entries as $log_entry) {
if (trim($log_entry) != '') {
$date = mb_substr($log_entry, 0, 19);
$username = mb_substr(
$log_entry, 20, mb_strpos($log_entry, "\n") - 20
);
if ($first_iteration) {
$ddl_date_from = $date;
$first_iteration = false;
}
$statement = rtrim(mb_strstr($log_entry, "\n"));
$ddlog[] = array( 'date' => $date,
'username'=> $username,
'statement' => $statement );
}
}
$date_from = $ddl_date_from;
$ddl_date_to = $date;
$dml_date_from = $date_from;
$dmlog = array();
$first_iteration = true;
// Iterate tracked data manipulation statements
// For each log entry we want to get date, username and statement
foreach ($log_data_entries as $log_entry) {
if (trim($log_entry) != '') {
$date = mb_substr($log_entry, 0, 19);
$username = mb_substr(
$log_entry, 20, mb_strpos($log_entry, "\n") - 20
);
if ($first_iteration) {
$dml_date_from = $date;
$first_iteration = false;
}
$statement = rtrim(mb_strstr($log_entry, "\n"));
$dmlog[] = array( 'date' => $date,
'username' => $username,
'statement' => $statement );
}
}
$dml_date_to = $date;
// Define begin and end of date range for both logs
$data = array();
if (strtotime($ddl_date_from) <= strtotime($dml_date_from)) {
$data['date_from'] = $ddl_date_from;
} else {
$data['date_from'] = $dml_date_from;
}
if (strtotime($ddl_date_to) >= strtotime($dml_date_to)) {
$data['date_to'] = $ddl_date_to;
} else {
$data['date_to'] = $dml_date_to;
}
$data['ddlog'] = $ddlog;
$data['dmlog'] = $dmlog;
$data['tracking'] = $mixed['tracking'];
$data['schema_snapshot'] = $mixed['schema_snapshot'];
return $data;
}
/**
* Parses a query. Gets
* - statement identifier (UPDATE, ALTER TABLE, ...)
* - type of statement, is it part of DDL or DML ?
* - tablename
*
* @param string $query query
*
* @static
* @todo: using PMA SQL Parser when possible
* @todo: support multi-table/view drops
*
* @return mixed Array containing identifier, type and tablename.
*
*/
static public function parseQuery($query)
{
// Usage of PMA_SQP does not work here
//
// require_once("libraries/sqlparser.lib.php");
// $parsed_sql = PMA_SQP_parse($query);
// $sql_info = PMA_SQP_analyze($parsed_sql);
$query = str_replace("\n", " ", $query);
$query = str_replace("\r", " ", $query);
$query = trim($query);
$query = trim($query, ' -');
$tokens = explode(" ", $query);
foreach ($tokens as $key => $value) {
$tokens[$key] = mb_strtoupper($value);
}
// Parse USE statement, need it for SQL dump imports
if (mb_substr($query, 0, 4) == 'USE ') {
$prefix = explode('USE ', $query);
$GLOBALS['db'] = self::getTableName($prefix[1]);
}
/*
* DDL statements
*/
$result = array();
$result['type'] = 'DDL';
// Parse CREATE VIEW statement
if (in_array('CREATE', $tokens) == true
&& in_array('VIEW', $tokens) == true
&& in_array('AS', $tokens) == true
) {
$result['identifier'] = 'CREATE VIEW';
$index = array_search('VIEW', $tokens);
$result['tablename'] = mb_strtolower(
self::getTableName($tokens[$index + 1])
);
}
// Parse ALTER VIEW statement
if (in_array('ALTER', $tokens) == true
&& in_array('VIEW', $tokens) == true
&& in_array('AS', $tokens) == true
&& ! isset($result['identifier'])
) {
$result['identifier'] = 'ALTER VIEW';
$index = array_search('VIEW', $tokens);
$result['tablename'] = mb_strtolower(
self::getTableName($tokens[$index + 1])
);
}
// Parse DROP VIEW statement
if (! isset($result['identifier'])
&& substr($query, 0, 10) == 'DROP VIEW '
) {
$result['identifier'] = 'DROP VIEW';
$prefix = explode('DROP VIEW ', $query);
$str = str_replace('IF EXISTS', '', $prefix[1]);
$result['tablename'] = self::getTableName($str);
}
// Parse CREATE DATABASE statement
if (! isset($result['identifier'])
&& substr($query, 0, 15) == 'CREATE DATABASE'
) {
$result['identifier'] = 'CREATE DATABASE';
$str = str_replace('CREATE DATABASE', '', $query);
$str = str_replace('IF NOT EXISTS', '', $str);
$prefix = explode('DEFAULT ', $str);
$result['tablename'] = '';
$GLOBALS['db'] = self::getTableName($prefix[0]);
}
// Parse ALTER DATABASE statement
if (! isset($result['identifier'])
&& substr($query, 0, 14) == 'ALTER DATABASE'
) {
$result['identifier'] = 'ALTER DATABASE';
$result['tablename'] = '';
}
// Parse DROP DATABASE statement
if (! isset($result['identifier'])
&& substr($query, 0, 13) == 'DROP DATABASE'
) {
$result['identifier'] = 'DROP DATABASE';
$str = str_replace('DROP DATABASE', '', $query);
$str = str_replace('IF EXISTS', '', $str);
$GLOBALS['db'] = self::getTableName($str);
$result['tablename'] = '';
}
// Parse CREATE TABLE statement
if (! isset($result['identifier'])
&& substr($query, 0, 12) == 'CREATE TABLE'
) {
$result['identifier'] = 'CREATE TABLE';
$query = str_replace('IF NOT EXISTS', '', $query);
$prefix = explode('CREATE TABLE ', $query);
$suffix = explode('(', $prefix[1]);
$result['tablename'] = self::getTableName($suffix[0]);
}
// Parse ALTER TABLE statement
if (! isset($result['identifier'])
&& substr($query, 0, 12) == 'ALTER TABLE '
) {
$result['identifier'] = 'ALTER TABLE';
$prefix = explode('ALTER TABLE ', $query);
$suffix = explode(' ', $prefix[1]);
$result['tablename'] = self::getTableName($suffix[0]);
}
// Parse DROP TABLE statement
if (! isset($result['identifier'])
&& substr($query, 0, 11) == 'DROP TABLE '
) {
$result['identifier'] = 'DROP TABLE';
$prefix = explode('DROP TABLE ', $query);
$str = str_replace('IF EXISTS', '', $prefix[1]);
$result['tablename'] = self::getTableName($str);
}
// Parse CREATE INDEX statement
if (! isset($result['identifier'])
&& (substr($query, 0, 12) == 'CREATE INDEX'
|| substr($query, 0, 19) == 'CREATE UNIQUE INDEX'
|| substr($query, 0, 20) == 'CREATE SPATIAL INDEX')
) {
$result['identifier'] = 'CREATE INDEX';
$prefix = explode('ON ', $query);
$suffix = explode('(', $prefix[1]);
$result['tablename'] = self::getTableName($suffix[0]);
}
// Parse DROP INDEX statement
if (! isset($result['identifier'])
&& substr($query, 0, 10) == 'DROP INDEX'
) {
$result['identifier'] = 'DROP INDEX';
$prefix = explode('ON ', $query);
$result['tablename'] = self::getTableName($prefix[1]);
}
// Parse RENAME TABLE statement
if (! isset($result['identifier'])
&& substr($query, 0, 13) == 'RENAME TABLE '
) {
$result['identifier'] = 'RENAME TABLE';
$prefix = explode('RENAME TABLE ', $query);
$names = explode(' TO ', $prefix[1]);
$result['tablename'] = self::getTableName($names[0]);
$result["tablename_after_rename"] = self::getTableName($names[1]);
}
/*
* DML statements
*/
if (! isset($result['identifier'])) {
$result["type"] = 'DML';
}
// Parse UPDATE statement
if (! isset($result['identifier'])
&& substr($query, 0, 6) == 'UPDATE'
) {
$result['identifier'] = 'UPDATE';
$prefix = explode('UPDATE ', $query);
$suffix = explode(' ', $prefix[1]);
$result['tablename'] = self::getTableName($suffix[0]);
}
// Parse INSERT INTO statement
if (! isset($result['identifier'])
&& substr($query, 0, 11) == 'INSERT INTO'
) {
$result['identifier'] = 'INSERT';
$prefix = explode('INSERT INTO', $query);
$suffix = explode('(', $prefix[1]);
$result['tablename'] = self::getTableName($suffix[0]);
}
// Parse DELETE statement
if (! isset($result['identifier'])
&& substr($query, 0, 6) == 'DELETE'
) {
$result['identifier'] = 'DELETE';
$prefix = explode('FROM ', $query);
$suffix = explode(' ', $prefix[1]);
$result['tablename'] = self::getTableName($suffix[0]);
}
// Parse TRUNCATE statement
if (! isset($result['identifier'])
&& substr($query, 0, 8) == 'TRUNCATE'
) {
$result['identifier'] = 'TRUNCATE';
$prefix = explode('TRUNCATE', $query);
$result['tablename'] = self::getTableName($prefix[1]);
}
return $result;
}
/**
* Analyzes a given SQL statement and saves tracking data.
*
* @param string $query a SQL query
*
* @static
*
* @return void
*/
static public function handleQuery($query)
{
// If query is marked as untouchable, leave
if (mb_strstr($query, "/*NOTRACK*/")) {
return;
}
if (! (substr($query, -1) == ';')) {
$query = $query . ";\n";
}
// Get some information about query
$result = self::parseQuery($query);
// Get database name
$dbname = trim(isset($GLOBALS['db']) ? $GLOBALS['db'] : '', '`');
// $dbname can be empty, for example when coming from Synchronize
// and this is a query for the remote server
if (empty($dbname)) {
return;
}
// If we found a valid statement
if (isset($result['identifier'])) {
$version = self::getVersion(
$dbname, $result['tablename'], $result['identifier']
);
// If version not exists and auto-creation is enabled
if ($GLOBALS['cfg']['Server']['tracking_version_auto_create'] == true
&& self::isTracked($dbname, $result['tablename']) == false
&& $version == -1
) {
// Create the version
switch ($result['identifier']) {
case 'CREATE TABLE':
self::createVersion($dbname, $result['tablename'], '1');
break;
case 'CREATE VIEW':
self::createVersion(
$dbname, $result['tablename'], '1', '', true
);
break;
case 'CREATE DATABASE':
self::createDatabaseVersion($dbname, '1', $query);
break;
} // end switch
}
// If version exists
if (self::isTracked($dbname, $result['tablename']) && $version != -1) {
if ($result['type'] == 'DDL') {
$save_to = 'schema_sql';
} elseif ($result['type'] == 'DML') {
$save_to = 'data_sql';
} else {
$save_to = '';
}
$date = date('Y-m-d H:i:s');
// Cut off `dbname`. from query
$query = preg_replace(
'/`' . preg_quote($dbname, '/') . '`\s?\./',
'',
$query
);
// Add log information
$query = self::getLogComment() . $query ;
// Mark it as untouchable
$sql_query = " /*NOTRACK*/\n"
. " UPDATE " . self::_getTrackingTable()
. " SET " . Util::backquote($save_to)
. " = CONCAT( " . Util::backquote($save_to) . ",'\n"
. $GLOBALS['dbi']->escapeString($query) . "') ,"
. " `date_updated` = '" . $date . "' ";
// If table was renamed we have to change
// the tablename attribute in pma_tracking too
if ($result['identifier'] == 'RENAME TABLE') {
$sql_query .= ', `table_name` = \''
. $GLOBALS['dbi']->escapeString($result['tablename_after_rename'])
. '\' ';
}
// Save the tracking information only for
// 1. the database
// 2. the table / view
// 3. the statements
// we want to track
$sql_query .=
" WHERE FIND_IN_SET('" . $result['identifier'] . "',tracking) > 0" .
" AND `db_name` = '" . $GLOBALS['dbi']->escapeString($dbname) . "' " .
" AND `table_name` = '"
. $GLOBALS['dbi']->escapeString($result['tablename']) . "' " .
" AND `version` = '" . $GLOBALS['dbi']->escapeString($version) . "' ";
PMA_queryAsControlUser($sql_query);
}
}
}
/**
* Returns the tracking table
*
* @return string tracking table
*/
private static function _getTrackingTable()
{
$cfgRelation = PMA_getRelationsParam();
return Util::backquote($cfgRelation['db'])
. '.' . Util::backquote($cfgRelation['tracking']);
}
}

296
#pma/libraries/Types.php Normal file
View File

@ -0,0 +1,296 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* SQL data types definition
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Generic class holding type definitions.
*
* @package PhpMyAdmin
*/
class Types
{
/**
* Returns list of unary operators.
*
* @return string[]
*/
public function getUnaryOperators()
{
return array(
'IS NULL',
'IS NOT NULL',
"= ''",
"!= ''",
);
}
/**
* Check whether operator is unary.
*
* @param string $op operator name
*
* @return boolean
*/
public function isUnaryOperator($op)
{
return in_array($op, $this->getUnaryOperators());
}
/**
* Returns list of operators checking for NULL.
*
* @return string[]
*/
public function getNullOperators()
{
return array(
'IS NULL',
'IS NOT NULL',
);
}
/**
* ENUM search operators
*
* @return string[]
*/
public function getEnumOperators()
{
return array(
'=',
'!=',
);
}
/**
* TEXT search operators
*
* @return string[]
*/
public function getTextOperators()
{
return array(
'LIKE',
'LIKE %...%',
'NOT LIKE',
'=',
'!=',
'REGEXP',
'REGEXP ^...$',
'NOT REGEXP',
"= ''",
"!= ''",
'IN (...)',
'NOT IN (...)',
'BETWEEN',
'NOT BETWEEN',
);
}
/**
* Number search operators
*
* @return string[]
*/
public function getNumberOperators()
{
return array(
'=',
'>',
'>=',
'<',
'<=',
'!=',
'LIKE',
'LIKE %...%',
'NOT LIKE',
'IN (...)',
'NOT IN (...)',
'BETWEEN',
'NOT BETWEEN',
);
}
/**
* Returns operators for given type
*
* @param string $type Type of field
* @param boolean $null Whether field can be NULL
*
* @return string[]
*/
public function getTypeOperators($type, $null)
{
$ret = array();
$class = $this->getTypeClass($type);
if (strncasecmp($type, 'enum', 4) == 0) {
$ret = array_merge($ret, $this->getEnumOperators());
} elseif ($class == 'CHAR') {
$ret = array_merge($ret, $this->getTextOperators());
} else {
$ret = array_merge($ret, $this->getNumberOperators());
}
if ($null) {
$ret = array_merge($ret, $this->getNullOperators());
}
return $ret;
}
/**
* Returns operators for given type as html options
*
* @param string $type Type of field
* @param boolean $null Whether field can be NULL
* @param string $selectedOperator Option to be selected
*
* @return string Generated Html
*/
public function getTypeOperatorsHtml($type, $null, $selectedOperator = null)
{
$html = '';
foreach ($this->getTypeOperators($type, $null) as $fc) {
if (isset($selectedOperator) && $selectedOperator == $fc) {
$selected = ' selected="selected"';
} else {
$selected = '';
}
$html .= '<option value="' . htmlspecialchars($fc) . '"'
. $selected . '>'
. htmlspecialchars($fc) . '</option>';
}
return $html;
}
/**
* Returns the data type description.
*
* @param string $type The data type to get a description.
*
* @return string
*
*/
public function getTypeDescription($type)
{
return '';
}
/**
* Returns class of a type, used for functions available for type
* or default values.
*
* @param string $type The data type to get a class.
*
* @return string
*
*/
public function getTypeClass($type)
{
return '';
}
/**
* Returns array of functions available for a class.
*
* @param string $class The class to get function list.
*
* @return string[]
*
*/
public function getFunctionsClass($class)
{
return array();
}
/**
* Returns array of functions available for a type.
*
* @param string $type The data type to get function list.
*
* @return string[]
*
*/
public function getFunctions($type)
{
$class = $this->getTypeClass($type);
return $this->getFunctionsClass($class);
}
/**
* Returns array of all functions available.
*
* @return string[]
*
*/
public function getAllFunctions()
{
$ret = array_merge(
$this->getFunctionsClass('CHAR'),
$this->getFunctionsClass('NUMBER'),
$this->getFunctionsClass('DATE'),
$this->getFunctionsClass('UUID')
);
sort($ret);
return $ret;
}
/**
* Returns array of all attributes available.
*
* @return string[]
*
*/
public function getAttributes()
{
return array();
}
/**
* Returns array of all column types available.
*
* @return string[]
*
*/
public function getColumns()
{
// most used types
return array(
'INT',
'VARCHAR',
'TEXT',
'DATE',
);
}
/**
* Returns an array of integer types
*
* @return string[] integer types
*/
public function getIntegerTypes()
{
return array();
}
/**
* Returns the min and max values of a given integer type
*
* @param string $type integer type
* @param boolean $signed whether signed
*
* @return string[] min and max values
*/
public function getIntegerRange($type, $signed = true)
{
return array('', '');
}
}

View File

@ -0,0 +1,590 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* SQL data types definition
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
/**
* Class holding type definitions for MySQL.
*
* @package PhpMyAdmin
*/
class TypesMySQL extends Types
{
/**
* Returns the data type description.
*
* @param string $type The data type to get a description.
*
* @return string
*
*/
public function getTypeDescription($type)
{
$type = mb_strtoupper($type);
switch ($type) {
case 'TINYINT':
return __(
'A 1-byte integer, signed range is -128 to 127, unsigned range is ' .
'0 to 255'
);
case 'SMALLINT':
return __(
'A 2-byte integer, signed range is -32,768 to 32,767, unsigned ' .
'range is 0 to 65,535'
);
case 'MEDIUMINT':
return __(
'A 3-byte integer, signed range is -8,388,608 to 8,388,607, ' .
'unsigned range is 0 to 16,777,215'
);
case 'INT':
return __(
'A 4-byte integer, signed range is ' .
'-2,147,483,648 to 2,147,483,647, unsigned range is 0 to ' .
'4,294,967,295'
);
case 'BIGINT':
return __(
'An 8-byte integer, signed range is -9,223,372,036,854,775,808 ' .
'to 9,223,372,036,854,775,807, unsigned range is 0 to ' .
'18,446,744,073,709,551,615'
);
case 'DECIMAL':
return __(
'A fixed-point number (M, D) - the maximum number of digits (M) ' .
'is 65 (default 10), the maximum number of decimals (D) is 30 ' .
'(default 0)'
);
case 'FLOAT':
return __(
'A small floating-point number, allowable values are ' .
'-3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to ' .
'3.402823466E+38'
);
case 'DOUBLE':
return __(
'A double-precision floating-point number, allowable values are ' .
'-1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and ' .
'2.2250738585072014E-308 to 1.7976931348623157E+308'
);
case 'REAL':
return __(
'Synonym for DOUBLE (exception: in REAL_AS_FLOAT SQL mode it is ' .
'a synonym for FLOAT)'
);
case 'BIT':
return __(
'A bit-field type (M), storing M of bits per value (default is 1, ' .
'maximum is 64)'
);
case 'BOOLEAN':
return __(
'A synonym for TINYINT(1), a value of zero is considered false, ' .
'nonzero values are considered true'
);
case 'SERIAL':
return __('An alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE');
case 'DATE':
return sprintf(
__('A date, supported range is %1$s to %2$s'), '1000-01-01',
'9999-12-31'
);
case 'DATETIME':
return sprintf(
__('A date and time combination, supported range is %1$s to %2$s'),
'1000-01-01 00:00:00', '9999-12-31 23:59:59'
);
case 'TIMESTAMP':
return __(
'A timestamp, range is 1970-01-01 00:00:01 UTC to 2038-01-09 ' .
'03:14:07 UTC, stored as the number of seconds since the epoch ' .
'(1970-01-01 00:00:00 UTC)'
);
case 'TIME':
return sprintf(
__('A time, range is %1$s to %2$s'), '-838:59:59', '838:59:59'
);
case 'YEAR':
return __(
"A year in four-digit (4, default) or two-digit (2) format, the " .
"allowable values are 70 (1970) to 69 (2069) or 1901 to 2155 and " .
"0000"
);
case 'CHAR':
return __(
'A fixed-length (0-255, default 1) string that is always ' .
'right-padded with spaces to the specified length when stored'
);
case 'VARCHAR':
return sprintf(
__(
'A variable-length (%s) string, the effective maximum length ' .
'is subject to the maximum row size'
), '0-65,535'
);
case 'TINYTEXT':
return __(
'A TEXT column with a maximum length of 255 (2^8 - 1) characters, ' .
'stored with a one-byte prefix indicating the length of the value ' .
'in bytes'
);
case 'TEXT':
return __(
'A TEXT column with a maximum length of 65,535 (2^16 - 1) ' .
'characters, stored with a two-byte prefix indicating the length ' .
'of the value in bytes'
);
case 'MEDIUMTEXT':
return __(
'A TEXT column with a maximum length of 16,777,215 (2^24 - 1) ' .
'characters, stored with a three-byte prefix indicating the ' .
'length of the value in bytes'
);
case 'LONGTEXT':
return __(
'A TEXT column with a maximum length of 4,294,967,295 or 4GiB ' .
'(2^32 - 1) characters, stored with a four-byte prefix indicating ' .
'the length of the value in bytes'
);
case 'BINARY':
return __(
'Similar to the CHAR type, but stores binary byte strings rather ' .
'than non-binary character strings'
);
case 'VARBINARY':
return __(
'Similar to the VARCHAR type, but stores binary byte strings ' .
'rather than non-binary character strings'
);
case 'TINYBLOB':
return __(
'A BLOB column with a maximum length of 255 (2^8 - 1) bytes, ' .
'stored with a one-byte prefix indicating the length of the value'
);
case 'MEDIUMBLOB':
return __(
'A BLOB column with a maximum length of 16,777,215 (2^24 - 1) ' .
'bytes, stored with a three-byte prefix indicating the length of ' .
'the value'
);
case 'BLOB':
return __(
'A BLOB column with a maximum length of 65,535 (2^16 - 1) bytes, ' .
'stored with a two-byte prefix indicating the length of the value'
);
case 'LONGBLOB':
return __(
'A BLOB column with a maximum length of 4,294,967,295 or 4GiB ' .
'(2^32 - 1) bytes, stored with a four-byte prefix indicating the ' .
'length of the value'
);
case 'ENUM':
return __(
"An enumeration, chosen from the list of up to 65,535 values or " .
"the special '' error value"
);
case 'SET':
return __("A single value chosen from a set of up to 64 members");
case 'GEOMETRY':
return __('A type that can store a geometry of any type');
case 'POINT':
return __('A point in 2-dimensional space');
case 'LINESTRING':
return __('A curve with linear interpolation between points');
case 'POLYGON':
return __('A polygon');
case 'MULTIPOINT':
return __('A collection of points');
case 'MULTILINESTRING':
return __(
'A collection of curves with linear interpolation between points'
);
case 'MULTIPOLYGON':
return __('A collection of polygons');
case 'GEOMETRYCOLLECTION':
return __('A collection of geometry objects of any type');
case 'JSON':
return __(
'Stores and enables efficient access to data in JSON'
. ' (JavaScript Object Notation) documents'
);
}
return '';
}
/**
* Returns class of a type, used for functions available for type
* or default values.
*
* @param string $type The data type to get a class.
*
* @return string
*
*/
public function getTypeClass($type)
{
$type = mb_strtoupper($type);
switch ($type) {
case 'TINYINT':
case 'SMALLINT':
case 'MEDIUMINT':
case 'INT':
case 'BIGINT':
case 'DECIMAL':
case 'FLOAT':
case 'DOUBLE':
case 'REAL':
case 'BIT':
case 'BOOLEAN':
case 'SERIAL':
return 'NUMBER';
case 'DATE':
case 'DATETIME':
case 'TIMESTAMP':
case 'TIME':
case 'YEAR':
return 'DATE';
case 'CHAR':
case 'VARCHAR':
case 'TINYTEXT':
case 'TEXT':
case 'MEDIUMTEXT':
case 'LONGTEXT':
case 'BINARY':
case 'VARBINARY':
case 'TINYBLOB':
case 'MEDIUMBLOB':
case 'BLOB':
case 'LONGBLOB':
case 'ENUM':
case 'SET':
return 'CHAR';
case 'GEOMETRY':
case 'POINT':
case 'LINESTRING':
case 'POLYGON':
case 'MULTIPOINT':
case 'MULTILINESTRING':
case 'MULTIPOLYGON':
case 'GEOMETRYCOLLECTION':
return 'SPATIAL';
case 'JSON':
return 'JSON';
}
return '';
}
/**
* Returns array of functions available for a class.
*
* @param string $class The class to get function list.
*
* @return string[]
*
*/
public function getFunctionsClass($class)
{
switch ($class) {
case 'CHAR':
$ret = array(
'AES_DECRYPT',
'AES_ENCRYPT',
'BIN',
'CHAR',
'COMPRESS',
'CURRENT_USER',
'DATABASE',
'DAYNAME',
'DES_DECRYPT',
'DES_ENCRYPT',
'ENCRYPT',
'HEX',
'INET6_NTOA',
'INET_NTOA',
'LOAD_FILE',
'LOWER',
'LTRIM',
'MD5',
'MONTHNAME',
'OLD_PASSWORD',
'PASSWORD',
'QUOTE',
'REVERSE',
'RTRIM',
'SHA1',
'SOUNDEX',
'SPACE',
'TRIM',
'UNCOMPRESS',
'UNHEX',
'UPPER',
'USER',
'UUID',
'VERSION',
);
if ((PMA_MARIADB && PMA_MYSQL_INT_VERSION < 100012)
|| PMA_MYSQL_INT_VERSION < 50603
) {
$ret = array_diff($ret, array('INET6_NTOA'));
}
return $ret;
case 'DATE':
return array(
'CURRENT_DATE',
'CURRENT_TIME',
'DATE',
'FROM_DAYS',
'FROM_UNIXTIME',
'LAST_DAY',
'NOW',
'SEC_TO_TIME',
'SYSDATE',
'TIME',
'TIMESTAMP',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'YEAR',
);
case 'NUMBER':
$ret = array(
'ABS',
'ACOS',
'ASCII',
'ASIN',
'ATAN',
'BIT_LENGTH',
'BIT_COUNT',
'CEILING',
'CHAR_LENGTH',
'CONNECTION_ID',
'COS',
'COT',
'CRC32',
'DAYOFMONTH',
'DAYOFWEEK',
'DAYOFYEAR',
'DEGREES',
'EXP',
'FLOOR',
'HOUR',
'INET6_ATON',
'INET_ATON',
'LENGTH',
'LN',
'LOG',
'LOG2',
'LOG10',
'MICROSECOND',
'MINUTE',
'MONTH',
'OCT',
'ORD',
'PI',
'QUARTER',
'RADIANS',
'RAND',
'ROUND',
'SECOND',
'SIGN',
'SIN',
'SQRT',
'TAN',
'TO_DAYS',
'TO_SECONDS',
'TIME_TO_SEC',
'UNCOMPRESSED_LENGTH',
'UNIX_TIMESTAMP',
'UUID_SHORT',
'WEEK',
'WEEKDAY',
'WEEKOFYEAR',
'YEARWEEK',
);
if ((PMA_MARIADB && PMA_MYSQL_INT_VERSION < 100012)
|| PMA_MYSQL_INT_VERSION < 50603
) {
$ret = array_diff($ret, array('INET6_ATON'));
}
return $ret;
case 'SPATIAL':
return array(
'GeomFromText',
'GeomFromWKB',
'GeomCollFromText',
'LineFromText',
'MLineFromText',
'PointFromText',
'MPointFromText',
'PolyFromText',
'MPolyFromText',
'GeomCollFromWKB',
'LineFromWKB',
'MLineFromWKB',
'PointFromWKB',
'MPointFromWKB',
'PolyFromWKB',
'MPolyFromWKB',
);
}
return array();
}
/**
* Returns array of all attributes available.
*
* @return string[]
*
*/
public function getAttributes()
{
return array(
'',
'BINARY',
'UNSIGNED',
'UNSIGNED ZEROFILL',
'on update CURRENT_TIMESTAMP',
);
}
/**
* Returns array of all column types available.
*
* VARCHAR, TINYINT, TEXT and DATE are listed first, based on
* estimated popularity.
*
* @return string[]
*
*/
public function getColumns()
{
$ret = parent::getColumns();
// numeric
$ret[_pgettext('numeric types', 'Numeric')] = array(
'TINYINT',
'SMALLINT',
'MEDIUMINT',
'INT',
'BIGINT',
'-',
'DECIMAL',
'FLOAT',
'DOUBLE',
'REAL',
'-',
'BIT',
'BOOLEAN',
'SERIAL',
);
// Date/Time
$ret[_pgettext('date and time types', 'Date and time')] = array(
'DATE',
'DATETIME',
'TIMESTAMP',
'TIME',
'YEAR',
);
// Text
$ret[_pgettext('string types', 'String')] = array(
'CHAR',
'VARCHAR',
'-',
'TINYTEXT',
'TEXT',
'MEDIUMTEXT',
'LONGTEXT',
'-',
'BINARY',
'VARBINARY',
'-',
'TINYBLOB',
'MEDIUMBLOB',
'BLOB',
'LONGBLOB',
'-',
'ENUM',
'SET',
);
$ret[_pgettext('spatial types', 'Spatial')] = array(
'GEOMETRY',
'POINT',
'LINESTRING',
'POLYGON',
'MULTIPOINT',
'MULTILINESTRING',
'MULTIPOLYGON',
'GEOMETRYCOLLECTION',
);
if (PMA_MYSQL_INT_VERSION >= 50708
&& \PMA\libraries\Util::getServerType() != 'MariaDB'
) {
$ret['JSON'] = array(
'JSON',
);
}
return $ret;
}
/**
* Returns an array of integer types
*
* @return string[] integer types
*/
public function getIntegerTypes()
{
return array('tinyint', 'smallint', 'mediumint', 'int', 'bigint');
}
/**
* Returns the min and max values of a given integer type
*
* @param string $type integer type
* @param boolean $signed whether signed
*
* @return string[] min and max values
*/
public function getIntegerRange($type, $signed = true)
{
static $min_max_data = array(
'unsigned' => array(
'tinyint' => array('0', '255'),
'smallint' => array('0', '65535'),
'mediumint' => array('0', '16777215'),
'int' => array('0', '4294967295'),
'bigint' => array('0', '18446744073709551615')
),
'signed' => array(
'tinyint' => array('-128', '127'),
'smallint' => array('-32768', '32767'),
'mediumint' => array('-8388608', '8388607'),
'int' => array('-2147483648', '2147483647'),
'bigint' => array('-9223372036854775808', '9223372036854775807')
)
);
$relevantArray = $signed
? $min_max_data['signed']
: $min_max_data['unsigned'];
return isset($relevantArray[$type]) ? $relevantArray[$type] : array('', '');
}
}

4978
#pma/libraries/Util.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,278 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Responsible for retrieving version information and notifiying about latest version
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
use PMA\libraries\Util;
use stdClass;
if (!defined('PHPMYADMIN')) {
exit;
}
/**
* Responsible for retrieving version information and notifiying about latest version
*
* @package PhpMyAdmin
*
*/
class VersionInformation
{
/**
* Returns information with latest version from phpmyadmin.net
*
* @return object JSON decoded object with the data
*/
public function getLatestVersion()
{
if (!$GLOBALS['cfg']['VersionCheck']) {
return null;
}
// wait 3s at most for server response, it's enough to get information
// from a working server
$connection_timeout = 3;
$response = '{}';
// Get response text from phpmyadmin.net or from the session
// Update cache every 6 hours
if (isset($_SESSION['cache']['version_check'])
&& time() < $_SESSION['cache']['version_check']['timestamp'] + 3600 * 6
) {
$save = false;
$response = $_SESSION['cache']['version_check']['response'];
} else {
$save = true;
if (! defined('TESTSUITE')) {
session_write_close();
}
$file = 'https://www.phpmyadmin.net/home_page/version.json';
if (function_exists('curl_init')) {
$curl_handle = curl_init($file);
if ($curl_handle === false) {
return null;
}
$curl_handle = Util::configureCurl($curl_handle);
curl_setopt(
$curl_handle,
CURLOPT_HEADER,
false
);
curl_setopt(
$curl_handle,
CURLOPT_RETURNTRANSFER,
true
);
curl_setopt(
$curl_handle,
CURLOPT_TIMEOUT,
$connection_timeout
);
$response = @curl_exec($curl_handle);
} else if (ini_get('allow_url_fopen')) {
$context = array(
'http' => array(
'request_fulluri' => true,
'timeout' => $connection_timeout,
)
);
$context = Util::handleContext($context);
$response = @file_get_contents(
$file,
false,
stream_context_create($context)
);
}
// Check possible failure of getting data
if ($response === false) {
$response = '{}';
}
}
/* Parse response */
$data = json_decode($response);
/* Basic sanity checking */
if (! is_object($data)
|| empty($data->version)
|| empty($data->releases)
|| empty($data->date)
) {
return null;
}
if ($save) {
if (! isset($_SESSION) && ! defined('TESTSUITE')) {
session_start();
}
$_SESSION['cache']['version_check'] = array(
'response' => $response,
'timestamp' => time()
);
}
return $data;
}
/**
* Calculates numerical equivalent of phpMyAdmin version string
*
* @param string $version version
*
* @return mixed false on failure, integer on success
*/
public function versionToInt($version)
{
$parts = explode('-', $version);
if (count($parts) > 1) {
$suffix = $parts[1];
} else {
$suffix = '';
}
$parts = explode('.', $parts[0]);
$result = 0;
if (count($parts) >= 1 && is_numeric($parts[0])) {
$result += 1000000 * $parts[0];
}
if (count($parts) >= 2 && is_numeric($parts[1])) {
$result += 10000 * $parts[1];
}
if (count($parts) >= 3 && is_numeric($parts[2])) {
$result += 100 * $parts[2];
}
if (count($parts) >= 4 && is_numeric($parts[3])) {
$result += 1 * $parts[3];
}
if (!empty($suffix)) {
$matches = array();
if (preg_match('/^(\D+)(\d+)$/', $suffix, $matches)) {
$suffix = $matches[1];
$result += intval($matches[2]);
}
switch ($suffix) {
case 'pl':
$result += 60;
break;
case 'rc':
$result += 30;
break;
case 'beta':
$result += 20;
break;
case 'alpha':
$result += 10;
break;
case 'dev':
$result += 0;
break;
}
} else {
$result += 50; // for final
}
return $result;
}
/**
* Returns the version and date of the latest phpMyAdmin version compatible
* with the available PHP and MySQL versions
*
* @param array $releases array of information related to each version
*
* @return array containing the version and date of latest compatible version
*/
public function getLatestCompatibleVersion($releases)
{
foreach ($releases as $release) {
$phpVersions = $release->php_versions;
$phpConditions = explode(",", $phpVersions);
foreach ($phpConditions as $phpCondition) {
if (! $this->evaluateVersionCondition("PHP", $phpCondition)) {
continue 2;
}
}
// We evalute MySQL version constraint if there are only
// one server configured.
if (count($GLOBALS['cfg']['Servers']) == 1) {
$mysqlVersions = $release->mysql_versions;
$mysqlConditions = explode(",", $mysqlVersions);
foreach ($mysqlConditions as $mysqlCondition) {
if (!$this->evaluateVersionCondition('MySQL', $mysqlCondition)) {
continue 2;
}
}
}
return array(
'version' => $release->version,
'date' => $release->date,
);
}
// no compatible version
return null;
}
/**
* Checks whether PHP or MySQL version meets supplied version condition
*
* @param string $type PHP or MySQL
* @param string $condition version condition
*
* @return boolean whether the condition is met
*/
public function evaluateVersionCondition($type, $condition)
{
$operator = null;
$operators = array("<=", ">=", "!=", "<>", "<", ">", "="); // preserve order
foreach ($operators as $oneOperator) {
if (strpos($condition, $oneOperator) === 0) {
$operator = $oneOperator;
$version = substr($condition, strlen($oneOperator));
break;
}
}
$myVersion = null;
if ($type == 'PHP') {
$myVersion = $this->getPHPVersion();
} elseif ($type == 'MySQL') {
$myVersion = $this->getMySQLVersion();
}
if ($myVersion != null && $operator != null) {
return version_compare($myVersion, $version, $operator);
}
return false;
}
/**
* Returns the PHP version
*
* @return string PHP version
*/
protected function getPHPVersion()
{
return PHP_VERSION;
}
/**
* Returns the MySQL version
*
* @return string MySQL version
*/
protected function getMySQLVersion()
{
return PMA_MYSQL_STR_VERSION;
}
}

206
#pma/libraries/ZipFile.php Normal file
View File

@ -0,0 +1,206 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Zip file creation
*
* @package PhpMyAdmin
*/
namespace PMA\libraries;
if (!defined('PHPMYADMIN')) {
exit;
}
/**
* Zip file creation class.
* Makes zip files.
*
* @access public
* @package PhpMyAdmin
* @see Official ZIP file format: https://www.pkware.com/support/zip-app-note
*/
class ZipFile
{
/**
* Whether to echo zip as it's built or return as string from -> file
*
* @var boolean $doWrite
*/
var $doWrite = false;
/**
* Array to store compressed data
*
* @var array $datasec
*/
var $datasec = array();
/**
* Central directory
*
* @var array $ctrl_dir
*/
var $ctrl_dir = array();
/**
* End of central directory record
*
* @var string $eof_ctrl_dir
*/
var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
/**
* Last offset position
*
* @var integer $old_offset
*/
var $old_offset = 0;
/**
* Sets member variable this -> doWrite to true
* - Should be called immediately after class instantiation
* - If set to true, then ZIP archive are echo'ed to STDOUT as each
* file is added via this -> addfile(), and central directories are
* echoed to STDOUT on final call to this -> file(). Also,
* this -> file() returns an empty string so it is safe to issue a
* "echo $zipfile;" command
*
* @access public
*
* @return void
*/
function setDoWrite()
{
$this->doWrite = true;
} // end of the 'setDoWrite()' method
/**
* Converts an Unix timestamp to a four byte DOS date and time format (date
* in high two bytes, time in low two bytes allowing magnitude comparison).
*
* @param integer $unixtime the current Unix timestamp
*
* @return integer the current date in a four byte DOS format
*
* @access private
*/
function unix2DosTime($unixtime = 0)
{
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
} // end if
return (($timearray['year'] - 1980) << 25)
| ($timearray['mon'] << 21)
| ($timearray['mday'] << 16)
| ($timearray['hours'] << 11)
| ($timearray['minutes'] << 5)
| ($timearray['seconds'] >> 1);
} // end of the 'unix2DosTime()' method
/**
* Adds "file" to archive
*
* @param string $data file contents
* @param string $name name of the file in the archive (may contains the path)
* @param integer $time the current timestamp
*
* @access public
*
* @return void
*/
function addFile($data, $name, $time = 0)
{
$name = str_replace('\\', '/', $name);
$hexdtime = pack('V', $this->unix2DosTime($time));
$fr = "\x50\x4b\x03\x04";
$fr .= "\x14\x00"; // ver needed to extract
$fr .= "\x00\x00"; // gen purpose bit flag
$fr .= "\x08\x00"; // compression method
$fr .= $hexdtime; // last mod time and date
// "local file header" segment
$unc_len = strlen($data);
$crc = crc32($data);
$zdata = gzcompress($data);
$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
$c_len = strlen($zdata);
$fr .= pack('V', $crc); // crc32
$fr .= pack('V', $c_len); // compressed filesize
$fr .= pack('V', $unc_len); // uncompressed filesize
$fr .= pack('v', strlen($name)); // length of filename
$fr .= pack('v', 0); // extra field length
$fr .= $name;
// "file data" segment
$fr .= $zdata;
// echo this entry on the fly, ...
if ($this->doWrite) {
echo $fr;
} else { // ... OR add this entry to array
$this->datasec[] = $fr;
}
// now add to central directory record
$cdrec = "\x50\x4b\x01\x02";
$cdrec .= "\x00\x00"; // version made by
$cdrec .= "\x14\x00"; // version needed to extract
$cdrec .= "\x00\x00"; // gen purpose bit flag
$cdrec .= "\x08\x00"; // compression method
$cdrec .= $hexdtime; // last mod time & date
$cdrec .= pack('V', $crc); // crc32
$cdrec .= pack('V', $c_len); // compressed filesize
$cdrec .= pack('V', $unc_len); // uncompressed filesize
$cdrec .= pack('v', strlen($name)); // length of filename
$cdrec .= pack('v', 0); // extra field length
$cdrec .= pack('v', 0); // file comment length
$cdrec .= pack('v', 0); // disk number start
$cdrec .= pack('v', 0); // internal file attributes
$cdrec .= pack('V', 32); // external file attributes
// - 'archive' bit set
$cdrec .= pack('V', $this->old_offset); // relative offset of local header
$this->old_offset += strlen($fr);
$cdrec .= $name;
// optional extra field, file comment goes here
// save to central directory
$this->ctrl_dir[] = $cdrec;
} // end of the 'addFile()' method
/**
* Echo central dir if ->doWrite==true, else build string to return
*
* @return string if ->doWrite {empty string} else the ZIP file contents
*
* @access public
*/
function file()
{
$ctrldir = implode('', $this->ctrl_dir);
$header = $ctrldir .
$this->eof_ctrl_dir .
pack('v', sizeof($this->ctrl_dir)) . //total #of entries "on this disk"
pack('v', sizeof($this->ctrl_dir)) . //total #of entries overall
pack('V', strlen($ctrldir)) . //size of central dir
pack('V', $this->old_offset) . //offset to start of central dir
"\x00\x00"; //.zip file comment length
if ($this->doWrite) { // Send central directory & end ctrl dir to STDOUT
echo $header;
return ""; // Return empty string
} else { // Return entire ZIP archive as string
$data = implode('', $this->datasec);
return $data . $header;
}
} // end of the 'file()' method
} // end of the 'PMA\libraries\ZipFile' class

View File

@ -0,0 +1,69 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Contains Advisor functions
*
* @package PhpMyAdmin
*/
/**
* Formats interval like 10 per hour
*
* @param integer $num number to format
* @param integer $precision required precision
*
* @return string formatted string
*/
function ADVISOR_bytime($num, $precision)
{
if ($num >= 1) { // per second
$per = __('per second');
} elseif ($num * 60 >= 1) { // per minute
$num = $num * 60;
$per = __('per minute');
} elseif ($num * 60 * 60 >= 1 ) { // per hour
$num = $num * 60 * 60;
$per = __('per hour');
} else {
$num = $num * 60 * 60 * 24;
$per = __('per day');
}
$num = round($num, $precision);
if ($num == 0) {
$num = '<' . PMA\libraries\Util::pow(10, -$precision);
}
return "$num $per";
}
/**
* Wrapper for PMA\libraries\Util::timespanFormat
*
* This function is used when evaluating advisory_rules.txt
*
* @param int $seconds the timespan
*
* @return string the formatted value
*/
function ADVISOR_timespanFormat($seconds)
{
return PMA\libraries\Util::timespanFormat($seconds);
}
/**
* Wrapper around PMA\libraries\Util::formatByteDown
*
* This function is used when evaluating advisory_rules.txt
*
* @param double $value the value to format
* @param int $limes the sensitiveness
* @param int $comma the number of decimals to retain
*
* @return string the formatted value with unit
*/
function ADVISOR_formatByteDown($value, $limes = 6, $comma = 0)
{
return implode(' ', PMA\libraries\Util::formatByteDown($value, $limes, $comma));
}

View File

@ -0,0 +1,452 @@
# phpMyAdmin Advisory rules file
#
# Use only UNIX style newlines
#
# This file is being parsed by Advisor.php, which should handle syntax
# errors correctly. However, PHP Warnings and the like are being consumed by
# the phpMyAdmin error handler, so those won't show up E.g.: Justification line
# is empty because you used an unescape percent sign, sprintf() returns an
# empty string and no warning/error is shown
#
# Rule Syntax:
# 'rule' identifier[the name of the rule] eexpr [an optional precondition]
# expr [variable or value calculation used for the test]
# expr [test, if evaluted to 'true' it fires the rule. Use 'value' to insert the calculated value (without quotes)]
# string [the issue (what is the problem?)]
# string [the recommendation (how do i fix it?)]
# formatted-string '|' comma-seperated-expr [the justification (result of the calculated value / why did this rule fire?)]
# comma-seperated-expr: expr(,expr)*
# eexpr: [expr] - expr enclosed in []
# expr: a php code literal with extras:
# - variable names are replaced with their respective values
# - fired('name of rule') is replaced with true/false when given rule has
# been fired. Note however that this is a very simple rules engine.
# Rules are only checked in sequential order as they are written down
# here. If given rule has not been checked yet, fired() will always
# evaluate to false
# - 'value' is replaced with the calculated value. If it is a string, it
# will be put within single quotes
# - other than that you may use any php function, initialized variable or
# constant
#
# identifier: A string enclosed in single quotes
# string: A quoteless string, may contain HTML. Variable names enclosed in
# curly braces are replaced with links to directly edit this variable.
# e.g. {tmp_table_size}
# formatted-string: You may use classic php sprintf() string formatting here,
# the arguments must be appended after a trailing pipe (|) as
# mentioned in above syntax percent signs (%) are
# automatically escaped (%%) in the following cases: When
# followed by a space, dot or comma and at the end of the
# line)
#
# Comments start with #
#
# Queries
rule 'Uptime below one day'
Uptime
value < 86400
Uptime is less than 1 day, performance tuning may not be accurate.
To have more accurate averages it is recommended to let the server run for longer than a day before running this analyzer
The uptime is only %s | ADVISOR_timespanFormat(Uptime)
rule 'Questions below 1,000'
Questions
value < 1000
Fewer than 1,000 questions have been run against this server. The recommendations may not be accurate.
Let the server run for a longer time until it has executed a greater amount of queries.
Current amount of Questions: %s | Questions
rule 'Percentage of slow queries' [Questions > 0]
Slow_queries / Questions * 100
value >= 5
There is a lot of slow queries compared to the overall amount of Queries.
You might want to increase {long_query_time} or optimize the queries listed in the slow query log
The slow query rate should be below 5%, your value is %s%. | round(value,2)
rule 'Slow query rate' [Questions > 0]
(Slow_queries / Questions * 100) / Uptime
value * 60 * 60 > 1
There is a high percentage of slow queries compared to the server uptime.
You might want to increase {long_query_time} or optimize the queries listed in the slow query log
You have a slow query rate of %s per hour, you should have less than 1% per hour. | ADVISOR_bytime(value,2)
rule 'Long query time'
long_query_time
value >= 10
{long_query_time} is set to 10 seconds or more, thus only slow queries that take above 10 seconds are logged.
It is suggested to set {long_query_time} to a lower value, depending on your environment. Usually a value of 1-5 seconds is suggested.
long_query_time is currently set to %ds. | value
rule 'Slow query logging' [PMA_MYSQL_INT_VERSION < 50600]
log_slow_queries
value == 'OFF'
The slow query log is disabled.
Enable slow query logging by setting {log_slow_queries} to 'ON'. This will help troubleshooting badly performing queries.
log_slow_queries is set to 'OFF'
rule 'Slow query logging' [PMA_MYSQL_INT_VERSION >= 50600]
slow_query_log
value == 'OFF'
The slow query log is disabled.
Enable slow query logging by setting {slow_query_log} to 'ON'. This will help troubleshooting badly performing queries.
slow_query_log is set to 'OFF'
#
# versions
rule 'Release Series'
version
substr(value,0,2) <= '5.' && substr(value,2,1) < 1
The MySQL server version less than 5.1.
You should upgrade, as MySQL 5.1 has improved performance, and MySQL 5.5 even more so.
Current version: %s | value
rule 'Minor Version' [! fired('Release Series')]
version
substr(value,0,2) <= '5.' && substr(value,2,1) <= 1 && substr(value,4,2) < 30
Version less than 5.1.30 (the first GA release of 5.1).
You should upgrade, as recent versions of MySQL 5.1 have improved performance and MySQL 5.5 even more so.
Current version: %s | value
rule 'Minor Version' [! fired('Release Series')]
version
substr(value,0,1) == 5 && substr(value,2,1) == 5 && substr(value,4,2) < 8
Version less than 5.5.8 (the first GA release of 5.5).
You should upgrade, to a stable version of MySQL 5.5.
Current version: %s | value
rule 'Distribution'
version_comment
preg_match('/source/i',value)
Version is compiled from source, not a MySQL official binary.
If you did not compile from source, you may be using a package modified by a distribution. The MySQL manual only is accurate for official MySQL binaries, not any package distributions (such as RedHat, Debian/Ubuntu etc).
'source' found in version_comment
rule 'Distribution'
version_comment
preg_match('/percona/i',value)
The MySQL manual only is accurate for official MySQL binaries.
Percona documentation is at <a href="https://www.percona.com/software/documentation/">https://www.percona.com/software/documentation/</a>
'percona' found in version_comment
rule 'MySQL Architecture'
system_memory
value > 3072*1024 && !preg_match('/64/',version_compile_machine) && !preg_match('/64/',version_compile_os)
MySQL is not compiled as a 64-bit package.
Your memory capacity is above 3 GiB (assuming the Server is on localhost), so MySQL might not be able to access all of your memory. You might want to consider installing the 64-bit version of MySQL.
Available memory on this host: %s | ADVISOR_formatByteDown(value*1024, 2, 2)
#
# Query cache
# Lame: 'ON' == 0 is true, so you need to compare 'ON' == '0'
rule 'Query cache disabled'
query_cache_size
value == 0 || query_cache_type == 'OFF' || query_cache_type == '0'
The query cache is not enabled.
The query cache is known to greatly improve performance if configured correctly. Enable it by setting {query_cache_size} to a 2 digit MiB value and setting {query_cache_type} to 'ON'. <b>Note:</b> If you are using memcached, ignore this recommendation.
query_cache_size is set to 0 or query_cache_type is set to 'OFF'
rule 'Query caching method' [!fired('Query cache disabled')]
Questions / Uptime
value > 100
Suboptimal caching method.
You are using the MySQL Query cache with a fairly high traffic database. It might be worth considering to use <a href="https://dev.mysql.com/doc/refman/5.5/en/ha-memcached.html">memcached</a> instead of the MySQL Query cache, especially if you have multiple slaves.
The query cache is enabled and the server receives %d queries per second. This rule fires if there is more than 100 queries per second. | round(value,1)
rule 'Query cache efficiency (%)' [Com_select + Qcache_hits > 0 && !fired('Query cache disabled')]
Qcache_hits / (Com_select + Qcache_hits) * 100
value < 20
Query cache not running efficiently, it has a low hit rate.
Consider increasing {query_cache_limit}.
The current query cache hit rate of %s% is below 20% | round(value,1)
rule 'Query Cache usage' [!fired('Query cache disabled')]
100 - Qcache_free_memory / query_cache_size * 100
value < 80
Less than 80% of the query cache is being utilized.
This might be caused by {query_cache_limit} being too low. Flushing the query cache might help as well.
The current ratio of free query cache memory to total query cache size is %s%. It should be above 80% | round(value,1)
rule 'Query cache fragmentation' [!fired('Query cache disabled')]
Qcache_free_blocks / (Qcache_total_blocks / 2) * 100
value > 20
The query cache is considerably fragmented.
Severe fragmentation is likely to (further) increase Qcache_lowmem_prunes. This might be caused by many Query cache low memory prunes due to {query_cache_size} being too small. For a immediate but short lived fix you can flush the query cache (might lock the query cache for a long time). Carefully adjusting {query_cache_min_res_unit} to a lower value might help too, e.g. you can set it to the average size of your queries in the cache using this formula: (query_cache_size - qcache_free_memory) / qcache_queries_in_cache
The cache is currently fragmented by %s% , with 100% fragmentation meaning that the query cache is an alternating pattern of free and used blocks. This value should be below 20%. | round(value,1)
rule 'Query cache low memory prunes' [Qcache_inserts > 0 && !fired('Query cache disabled')]
Qcache_lowmem_prunes / Qcache_inserts * 100
value > 0.1
Cached queries are removed due to low query cache memory from the query cache.
You might want to increase {query_cache_size}, however keep in mind that the overhead of maintaining the cache is likely to increase with its size, so do this in small increments and monitor the results.
The ratio of removed queries to inserted queries is %s%. The lower this value is, the better (This rules firing limit: 0.1%) | round(value,1)
rule 'Query cache max size' [!fired('Query cache disabled')]
query_cache_size
value > 1024 * 1024 * 128
The query cache size is above 128 MiB. Big query caches may cause significant overhead that is required to maintain the cache.
Depending on your environment, it might be performance increasing to reduce this value.
Current query cache size: %s | ADVISOR_formatByteDown(value, 2, 2)
rule 'Query cache min result size' [!fired('Query cache disabled')]
query_cache_limit
value == 1024*1024
The max size of the result set in the query cache is the default of 1 MiB.
Changing {query_cache_limit} (usually by increasing) may increase efficiency. This variable determines the maximum size a query result may have to be inserted into the query cache. If there are many query results above 1 MiB that are well cacheable (many reads, little writes) then increasing {query_cache_limit} will increase efficiency. Whereas in the case of many query results being above 1 MiB that are not very well cacheable (often invalidated due to table updates) increasing {query_cache_limit} might reduce efficiency.
query_cache_limit is set to 1 MiB
#
# Sorts
rule 'Percentage of sorts that cause temporary tables' [Sort_scan + Sort_range > 0]
Sort_merge_passes / (Sort_scan + Sort_range) * 100
value > 10
Too many sorts are causing temporary tables.
Consider increasing {sort_buffer_size} and/or {read_rnd_buffer_size}, depending on your system memory limits.
%s% of all sorts cause temporary tables, this value should be lower than 10%. | round(value,1)
rule 'Rate of sorts that cause temporary tables'
Sort_merge_passes / Uptime
value * 60 * 60 > 1
Too many sorts are causing temporary tables.
Consider increasing {sort_buffer_size} and/or {read_rnd_buffer_size}, depending on your system memory limits.
Temporary tables average: %s, this value should be less than 1 per hour. | ADVISOR_bytime(value,2)
rule 'Sort rows'
Sort_rows / Uptime
value * 60 >= 1
There are lots of rows being sorted.
While there is nothing wrong with a high amount of row sorting, you might want to make sure that the queries which require a lot of sorting use indexed columns in the ORDER BY clause, as this will result in much faster sorting.
Sorted rows average: %s | ADVISOR_bytime(value,2)
# Joins, scans
rule 'Rate of joins without indexes'
(Select_range_check + Select_scan + Select_full_join) / Uptime
value * 60 * 60 > 1
There are too many joins without indexes.
This means that joins are doing full table scans. Adding indexes for the columns being used in the join conditions will greatly speed up table joins.
Table joins average: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Rate of reading first index entry'
Handler_read_first / Uptime
value * 60 * 60 > 1
The rate of reading the first index entry is high.
This usually indicates frequent full index scans. Full index scans are faster than table scans but require lots of CPU cycles in big tables, if those tables that have or had high volumes of UPDATEs and DELETEs, running 'OPTIMIZE TABLE' might reduce the amount of and/or speed up full index scans. Other than that full index scans can only be reduced by rewriting queries.
Index scans average: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Rate of reading fixed position'
Handler_read_rnd / Uptime
value * 60 * 60 > 1
The rate of reading data from a fixed position is high.
This indicates that many queries need to sort results and/or do a full table scan, including join queries that do not use indexes. Add indexes where applicable.
Rate of reading fixed position average: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Rate of reading next table row'
Handler_read_rnd_next / Uptime
value * 60 * 60 > 1
The rate of reading the next table row is high.
This indicates that many queries are doing full table scans. Add indexes where applicable.
Rate of reading next table row: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
# temp tables
rule 'Different tmp_table_size and max_heap_table_size'
tmp_table_size - max_heap_table_size
value !=0
{tmp_table_size} and {max_heap_table_size} are not the same.
If you have deliberately changed one of either: The server uses the lower value of either to determine the maximum size of in-memory tables. So if you wish to increase the in-memory table limit you will have to increase the other value as well.
Current values are tmp_table_size: %s, max_heap_table_size: %s | ADVISOR_formatByteDown(tmp_table_size, 2, 2), ADVISOR_formatByteDown(max_heap_table_size, 2, 2)
rule 'Percentage of temp tables on disk' [Created_tmp_tables + Created_tmp_disk_tables > 0]
Created_tmp_disk_tables / (Created_tmp_tables + Created_tmp_disk_tables) * 100
value > 25
Many temporary tables are being written to disk instead of being kept in memory.
Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To eliminate these you will have to rewrite your queries to avoid those conditions (Within a temporary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in the beginning of an <a href="https://www.facebook.com/note.php?note_id=10150111255065841&comments">Article by the Pythian Group</a>
%s% of all temporary tables are being written to disk, this value should be below 25% | round(value,1)
rule 'Temp disk rate' [!fired('Percentage of temp tables on disk')]
Created_tmp_disk_tables / Uptime
value * 60 * 60 > 1
Many temporary tables are being written to disk instead of being kept in memory.
Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To eliminate these you will have to rewrite your queries to avoid those conditions (Within a temporary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in the <a href="https://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html">MySQL Documentation</a>
Rate of temporary tables being written to disk: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
#
# MyISAM index cache
rule 'MyISAM key buffer size'
key_buffer_size
value == 0
Key buffer is not initialized. No MyISAM indexes will be cached.
Set {key_buffer_size} depending on the size of your MyISAM indexes. 64M is a good start.
key_buffer_size is 0
rule 'Max % MyISAM key buffer ever used' [key_buffer_size > 0]
Key_blocks_used * key_cache_block_size / key_buffer_size * 100
value < 95
MyISAM key buffer (index cache) % used is low.
You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used.
max % MyISAM key buffer ever used: %s%, this value should be above 95% | round(value,1)
# Don't fire if above rule fired - we don't need the same advice twice
rule 'Percentage of MyISAM key buffer used' [key_buffer_size > 0 && !fired('Max % MyISAM key buffer ever used')]
( 1 - Key_blocks_unused * key_cache_block_size / key_buffer_size) * 100
value < 95
MyISAM key buffer (index cache) % used is low.
You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used.
% MyISAM key buffer used: %s%, this value should be above 95% | round(value,1)
rule 'Percentage of index reads from memory' [Key_read_requests > 0]
100 - (Key_reads / Key_read_requests * 100)
value < 95
The % of indexes that use the MyISAM key buffer is low.
You may need to increase {key_buffer_size}.
Index reads from memory: %s%, this value should be above 95% | round(value,1)
#
# other caches
rule 'Rate of table open'
Opened_tables / Uptime
value*60*60 > 10
The rate of opening tables is high.
Opening tables requires disk I/O which is costly. Increasing {table_open_cache} might avoid this.
Opened table rate: %s, this value should be less than 10 per hour | ADVISOR_bytime(value,2)
rule 'Percentage of used open files limit'
Open_files / open_files_limit * 100
value > 85
The number of open files is approaching the max number of open files. You may get a "Too many open files" error.
Consider increasing {open_files_limit}, and check the error log when restarting after changing {open_files_limit}.
The number of opened files is at %s% of the limit. It should be below 85% | round(value,1)
rule 'Rate of open files'
Open_files / Uptime
value * 60 * 60 > 5
The rate of opening files is high.
Consider increasing {open_files_limit}, and check the error log when restarting after changing {open_files_limit}.
Opened files rate: %s, this value should be less than 5 per hour | ADVISOR_bytime(value,2)
rule 'Immediate table locks %' [Table_locks_waited + Table_locks_immediate > 0]
Table_locks_immediate / (Table_locks_waited + Table_locks_immediate) * 100
value < 95
Too many table locks were not granted immediately.
Optimize queries and/or use InnoDB to reduce lock wait.
Immediate table locks: %s%, this value should be above 95% | round(value,1)
rule 'Table lock wait rate'
Table_locks_waited / Uptime
value * 60 * 60 > 1
Too many table locks were not granted immediately.
Optimize queries and/or use InnoDB to reduce lock wait.
Table lock wait rate: %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Thread cache'
thread_cache_size
value < 1
Thread cache is disabled, resulting in more overhead from new connections to MySQL.
Enable the thread cache by setting {thread_cache_size} > 0.
The thread cache is set to 0
rule 'Thread cache hit rate %' [thread_cache_size > 0]
100 - Threads_created / Connections
value < 80
Thread cache is not efficient.
Increase {thread_cache_size}.
Thread cache hitrate: %s%, this value should be above 80% | round(value,1)
rule 'Threads that are slow to launch' [slow_launch_time > 0]
Slow_launch_threads
value > 0
There are too many threads that are slow to launch.
This generally happens in case of general system overload as it is pretty simple operations. You might want to monitor your system load carefully.
%s thread(s) took longer than %s seconds to start, it should be 0 | value, slow_launch_time
rule 'Slow launch time'
slow_launch_time
value > 2
Slow_launch_time is above 2s.
Set {slow_launch_time} to 1s or 2s to correctly count threads that are slow to launch.
slow_launch_time is set to %s | value
#
#Connections
rule 'Percentage of used connections'
Max_used_connections / max_connections * 100
value > 80
The maximum amount of used connections is getting close to the value of {max_connections}.
Increase {max_connections}, or decrease {wait_timeout} so that connections that do not close database handlers properly get killed sooner. Make sure the code closes database handlers properly.
Max_used_connections is at %s% of max_connections, it should be below 80% | round(value,1)
rule 'Percentage of aborted connections'
Aborted_connects / Connections * 100
value > 1
Too many connections are aborted.
Connections are usually aborted when they cannot be authorized. <a href="https://www.percona.com/blog/2008/08/23/how-to-track-down-the-source-of-aborted_connects/">This article</a> might help you track down the source.
%s% of all connections are aborted. This value should be below 1% | round(value,1)
rule 'Rate of aborted connections'
Aborted_connects / Uptime
value * 60 * 60 > 1
Too many connections are aborted.
Connections are usually aborted when they cannot be authorized. <a href="https://www.percona.com/blog/2008/08/23/how-to-track-down-the-source-of-aborted_connects/">This article</a> might help you track down the source.
Aborted connections rate is at %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
rule 'Percentage of aborted clients'
Aborted_clients / Connections * 100
value > 2
Too many clients are aborted.
Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code.
%s% of all clients are aborted. This value should be below 2% | round(value,1)
rule 'Rate of aborted clients'
Aborted_clients / Uptime
value * 60 * 60 > 1
Too many clients are aborted.
Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code.
Aborted client rate is at %s, this value should be less than 1 per hour | ADVISOR_bytime(value,2)
#
# InnoDB
rule 'Is InnoDB disabled?' [PMA_MYSQL_INT_VERSION < 50600]
have_innodb
value != "YES"
You do not have InnoDB enabled.
InnoDB is usually the better choice for table engines.
have_innodb is set to 'value'
rule 'InnoDB log size' [innodb_buffer_pool_size > 0]
innodb_log_file_size / innodb_buffer_pool_size * 100
value < 20 && innodb_log_file_size / (1024 * 1024) < 256
The InnoDB log file size is not an appropriate size, in relation to the InnoDB buffer pool.
Especially on a system with a lot of writes to InnoDB tables you should set {innodb_log_file_size} to 25% of {innodb_buffer_pool_size}. However the bigger this value, the longer the recovery time will be when database crashes, so this value should not be set much higher than 256 MiB. Please note however that you cannot simply change the value of this variable. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also <a href="https://mysqldatabaseadministration.blogspot.com/2007/01/increase-innodblogfilesize-proper-way.html">this blog entry</a>
Your InnoDB log size is at %s% in relation to the InnoDB buffer pool size, it should not be below 20% | round(value,1)
rule 'Max InnoDB log size' [innodb_buffer_pool_size > 0 && innodb_log_file_size / innodb_buffer_pool_size * 100 < 30]
innodb_log_file_size / (1024 * 1024)
value > 256
The InnoDB log file size is inadequately large.
It is usually sufficient to set {innodb_log_file_size} to 25% of the size of {innodb_buffer_pool_size}. A very big {innodb_log_file_size} slows down the recovery time after a database crash considerably. See also <a href="https://www.percona.com/blog/2006/07/03/choosing-proper-innodb_log_file_size/">this Article</a>. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also <a href="https://mysqldatabaseadministration.blogspot.com/2007/01/increase-innodblogfilesize-proper-way.html">this blog entry</a>
Your absolute InnoDB log size is %s MiB | round(value,1)
rule 'InnoDB buffer pool size' [system_memory > 0]
innodb_buffer_pool_size / system_memory * 100
value < 60
Your InnoDB buffer pool is fairly small.
The InnoDB buffer pool has a profound impact on performance for InnoDB tables. Assign all your remaining memory to this buffer. For database servers that use solely InnoDB as storage engine and have no other services (e.g. a web server) running, you may set this as high as 80% of your available memory. If that is not the case, you need to carefully assess the memory consumption of your other services and non-InnoDB-Tables and set this variable accordingly. If it is set too high, your system will start swapping, which decreases performance significantly. See also <a href="https://www.percona.com/blog/2007/11/03/choosing-innodb_buffer_pool_size/">this article</a>
You are currently using %s% of your memory for the InnoDB buffer pool. This rule fires if you are assigning less than 60%, however this might be perfectly adequate for your system if you don't have much InnoDB tables or other services running on the same machine. | value
#
# other
rule 'MyISAM concurrent inserts'
concurrent_insert
value === 0 || value === 'NEVER'
Enable {concurrent_insert} by setting it to 1
Setting {concurrent_insert} to 1 reduces contention between readers and writers for a given table. See also <a href="https://dev.mysql.com/doc/refman/5.5/en/concurrent-inserts.html">MySQL Documentation</a>
concurrent_insert is set to 0
# INSERT DELAYED USAGE
#Delayed_errors 0
#Delayed_insert_threads 0
#Delayed_writes 0
#Not_flushed_delayed_rows

View File

@ -0,0 +1,19 @@
<?php
/**
* Load class autoloader
*
* @package PhpMyAdmin
*/
require_once './libraries/Psr4Autoloader.php';
// instantiate the loader
$loader = \PMA\Psr4Autoloader::getInstance();
// register the autoloader
$loader->register();
// register the base directories for the namespace prefix
$loader->addNamespace('PMA', '.');
$loader->addNamespace('SqlParser', './libraries/sql-parser/src');
$loader->addNamespace('phpseclib', PHPSECLIB_INC_DIR);

View File

@ -0,0 +1,680 @@
<?php
/**
* BytesFall ShapeFiles library
*
* The library implements the 2D variants of the ShapeFile format as defined in
* http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf.
* The library currently supports reading and editing of ShapeFiles and the
* Associated information (DBF file).
*
* @package bfShapeFiles
* @version 0.0.2
* @link http://bfshapefiles.sourceforge.net/
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2-or-later
*
* Copyright 2006-2007 Ovidio <ovidio AT users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can download one from
* http://www.gnu.org/copyleft/gpl.html.
*
*/
function loadData($type, $data) {
if (!$data) return $data;
$tmp = unpack($type, $data);
return current($tmp);
}
function swap($binValue) {
$result = $binValue{strlen($binValue) - 1};
for($i = strlen($binValue) - 2; $i >= 0 ; $i--) {
$result .= $binValue{$i};
}
return $result;
}
function packDouble($value, $mode = 'LE') {
$value = (double)$value;
$bin = pack("d", $value);
//We test if the conversion of an integer (1) is done as LE or BE by default
switch (pack ('L', 1)) {
case pack ('V', 1): //Little Endian
$result = ($mode == 'LE') ? $bin : swap($bin);
break;
case pack ('N', 1): //Big Endian
$result = ($mode == 'BE') ? $bin : swap($bin);
break;
default: //Some other thing, we just return false
$result = FALSE;
}
return $result;
}
/**
* ShapeFile class
*
* @package bfShapeFiles
*/
class ShapeFile {
var $FileName;
var $SHPFile;
var $SHXFile;
var $DBFFile;
var $DBFHeader;
var $lastError = "";
var $boundingBox = array("xmin" => 0.0, "ymin" => 0.0, "xmax" => 0.0, "ymax" => 0.0);
var $fileLength = 0;
var $shapeType = 0;
var $records;
public function __construct($shapeType, $boundingBox = array("xmin" => 0.0, "ymin" => 0.0, "xmax" => 0.0, "ymax" => 0.0), $FileName = NULL) {
$this->shapeType = $shapeType;
$this->boundingBox = $boundingBox;
$this->FileName = $FileName;
$this->fileLength = 50;
}
function loadFromFile($FileName) {
$this->FileName = $FileName;
if (($this->_openSHPFile()) && ($this->_openDBFFile())) {
$this->_loadHeaders();
$this->_loadRecords();
$this->_closeSHPFile();
$this->_closeDBFFile();
} else {
return false;
}
}
function saveToFile($FileName = NULL) {
if ($FileName != NULL) $this->FileName = $FileName;
if (($this->_openSHPFile(TRUE)) && ($this->_openSHXFile(TRUE)) && ($this->_openDBFFile(TRUE))) {
$this->_saveHeaders();
$this->_saveRecords();
$this->_closeSHPFile();
$this->_closeSHXFile();
$this->_closeDBFFile();
} else {
return false;
}
}
function addRecord($record) {
if ((isset($this->DBFHeader)) && (is_array($this->DBFHeader))) {
$record->updateDBFInfo($this->DBFHeader);
}
$this->fileLength += ($record->getContentLength() + 4);
$this->records[] = $record;
$this->records[count($this->records) - 1]->recordNumber = count($this->records);
return (count($this->records) - 1);
}
function deleteRecord($index) {
if (isset($this->records[$index])) {
$this->fileLength -= ($this->records[$index]->getContentLength() + 4);
for ($i = $index; $i < (count($this->records) - 1); $i++) {
$this->records[$i] = $this->records[$i + 1];
}
unset($this->records[count($this->records) - 1]);
$this->_deleteRecordFromDBF($index);
}
}
function getDBFHeader() {
return $this->DBFHeader;
}
function setDBFHeader($header) {
$this->DBFHeader = $header;
for ($i = 0; $i < count($this->records); $i++) {
$this->records[$i]->updateDBFInfo($header);
}
}
function getIndexFromDBFData($field, $value) {
$result = -1;
for ($i = 0; $i < (count($this->records) - 1); $i++) {
if (isset($this->records[$i]->DBFData[$field]) && (strtoupper($this->records[$i]->DBFData[$field]) == strtoupper($value))) {
$result = $i;
}
}
return $result;
}
function _loadDBFHeader() {
$DBFFile = fopen(str_replace('.*', '.dbf', $this->FileName), 'r');
$result = array();
$buff32 = array();
$i = 1;
$inHeader = true;
while ($inHeader) {
if (!feof($DBFFile)) {
$buff32 = fread($DBFFile, 32);
if ($i > 1) {
if (substr($buff32, 0, 1) == chr(13)) {
$inHeader = false;
} else {
$pos = strpos(substr($buff32, 0, 10), chr(0));
$pos = ($pos == 0 ? 10 : $pos);
$fieldName = substr($buff32, 0, $pos);
$fieldType = substr($buff32, 11, 1);
$fieldLen = ord(substr($buff32, 16, 1));
$fieldDec = ord(substr($buff32, 17, 1));
array_push($result, array($fieldName, $fieldType, $fieldLen, $fieldDec));
}
}
$i++;
} else {
$inHeader = false;
}
}
fclose($DBFFile);
return($result);
}
function _deleteRecordFromDBF($index) {
if (@dbase_delete_record($this->DBFFile, $index)) {
@dbase_pack($this->DBFFile);
}
}
function _loadHeaders() {
fseek($this->SHPFile, 24, SEEK_SET);
$this->fileLength = loadData("N", fread($this->SHPFile, 4));
fseek($this->SHPFile, 32, SEEK_SET);
$this->shapeType = loadData("V", fread($this->SHPFile, 4));
$this->boundingBox = array();
$this->boundingBox["xmin"] = loadData("d", fread($this->SHPFile, 8));
$this->boundingBox["ymin"] = loadData("d", fread($this->SHPFile, 8));
$this->boundingBox["xmax"] = loadData("d", fread($this->SHPFile, 8));
$this->boundingBox["ymax"] = loadData("d", fread($this->SHPFile, 8));
$this->DBFHeader = $this->_loadDBFHeader();
}
function _saveHeaders() {
fwrite($this->SHPFile, pack("NNNNNN", 9994, 0, 0, 0, 0, 0));
fwrite($this->SHPFile, pack("N", $this->fileLength));
fwrite($this->SHPFile, pack("V", 1000));
fwrite($this->SHPFile, pack("V", $this->shapeType));
fwrite($this->SHPFile, packDouble($this->boundingBox['xmin']));
fwrite($this->SHPFile, packDouble($this->boundingBox['ymin']));
fwrite($this->SHPFile, packDouble($this->boundingBox['xmax']));
fwrite($this->SHPFile, packDouble($this->boundingBox['ymax']));
fwrite($this->SHPFile, pack("dddd", 0, 0, 0, 0));
fwrite($this->SHXFile, pack("NNNNNN", 9994, 0, 0, 0, 0, 0));
fwrite($this->SHXFile, pack("N", 50 + 4*count($this->records)));
fwrite($this->SHXFile, pack("V", 1000));
fwrite($this->SHXFile, pack("V", $this->shapeType));
fwrite($this->SHXFile, packDouble($this->boundingBox['xmin']));
fwrite($this->SHXFile, packDouble($this->boundingBox['ymin']));
fwrite($this->SHXFile, packDouble($this->boundingBox['xmax']));
fwrite($this->SHXFile, packDouble($this->boundingBox['ymax']));
fwrite($this->SHXFile, pack("dddd", 0, 0, 0, 0));
}
function _loadRecords() {
fseek($this->SHPFile, 100);
while (!feof($this->SHPFile)) {
$bByte = ftell($this->SHPFile);
$record = new ShapeRecord(-1);
$record->loadFromFile($this->SHPFile, $this->DBFFile);
$eByte = ftell($this->SHPFile);
if (($eByte <= $bByte) || ($record->lastError != "")) {
return false;
}
$this->records[] = $record;
}
}
function _saveRecords() {
if (file_exists(str_replace('.*', '.dbf', $this->FileName))) {
@unlink(str_replace('.*', '.dbf', $this->FileName));
}
if (!($this->DBFFile = @dbase_create(str_replace('.*', '.dbf', $this->FileName), $this->DBFHeader))) {
return $this->setError(sprintf("It wasn't possible to create the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
}
$offset = 50;
if (is_array($this->records) && (count($this->records) > 0)) {
reset($this->records);
while (list($index, $record) = each($this->records)) {
//Save the record to the .shp file
$record->saveToFile($this->SHPFile, $this->DBFFile, $index + 1);
//Save the record to the .shx file
fwrite($this->SHXFile, pack("N", $offset));
fwrite($this->SHXFile, pack("N", $record->getContentLength()));
$offset += (4 + $record->getContentLength());
}
}
@dbase_pack($this->DBFFile);
}
function _openSHPFile($toWrite = false) {
$this->SHPFile = @fopen(str_replace('.*', '.shp', $this->FileName), ($toWrite ? "wb+" : "rb"));
if (!$this->SHPFile) {
return $this->setError(sprintf("It wasn't possible to open the Shape file '%s'", str_replace('.*', '.shp', $this->FileName)));
}
return TRUE;
}
function _closeSHPFile() {
if ($this->SHPFile) {
fclose($this->SHPFile);
$this->SHPFile = NULL;
}
}
function _openSHXFile($toWrite = false) {
$this->SHXFile = @fopen(str_replace('.*', '.shx', $this->FileName), ($toWrite ? "wb+" : "rb"));
if (!$this->SHXFile) {
return $this->setError(sprintf("It wasn't possible to open the Index file '%s'", str_replace('.*', '.shx', $this->FileName)));
}
return TRUE;
}
function _closeSHXFile() {
if ($this->SHXFile) {
fclose($this->SHXFile);
$this->SHXFile = NULL;
}
}
function _openDBFFile($toWrite = false) {
$checkFunction = $toWrite ? "is_writable" : "is_readable";
if (($toWrite) && (!file_exists(str_replace('.*', '.dbf', $this->FileName)))) {
if (!@dbase_create(str_replace('.*', '.dbf', $this->FileName), $this->DBFHeader)) {
return $this->setError(sprintf("It wasn't possible to create the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
}
}
if ($checkFunction(str_replace('.*', '.dbf', $this->FileName))) {
$this->DBFFile = dbase_open(str_replace('.*', '.dbf', $this->FileName), ($toWrite ? 2 : 0));
if (!$this->DBFFile) {
return $this->setError(sprintf("It wasn't possible to open the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
}
} else {
return $this->setError(sprintf("It wasn't possible to find the DBase file '%s'", str_replace('.*', '.dbf', $this->FileName)));
}
return TRUE;
}
function _closeDBFFile() {
if ($this->DBFFile) {
dbase_close($this->DBFFile);
$this->DBFFile = NULL;
}
}
function setError($error) {
$this->lastError = $error;
return false;
}
}
class ShapeRecord {
var $SHPFile = NULL;
var $DBFFile = NULL;
var $recordNumber = NULL;
var $shapeType = NULL;
var $lastError = "";
var $SHPData = array();
var $DBFData = array();
public function __construct($shapeType) {
$this->shapeType = $shapeType;
}
function loadFromFile(&$SHPFile, &$DBFFile) {
$this->SHPFile = $SHPFile;
$this->DBFFile = $DBFFile;
$this->_loadHeaders();
switch ($this->shapeType) {
case 0:
$this->_loadNullRecord();
break;
case 1:
$this->_loadPointRecord();
break;
case 3:
$this->_loadPolyLineRecord();
break;
case 5:
$this->_loadPolygonRecord();
break;
case 8:
$this->_loadMultiPointRecord();
break;
default:
$this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
break;
}
$this->_loadDBFData();
}
function saveToFile(&$SHPFile, &$DBFFile, $recordNumber) {
$this->SHPFile = $SHPFile;
$this->DBFFile = $DBFFile;
$this->recordNumber = $recordNumber;
$this->_saveHeaders();
switch ($this->shapeType) {
case 0:
$this->_saveNullRecord();
break;
case 1:
$this->_savePointRecord();
break;
case 3:
$this->_savePolyLineRecord();
break;
case 5:
$this->_savePolygonRecord();
break;
case 8:
$this->_saveMultiPointRecord();
break;
default:
$this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
break;
}
$this->_saveDBFData();
}
function updateDBFInfo($header) {
$tmp = $this->DBFData;
unset($this->DBFData);
$this->DBFData = array();
reset($header);
while (list($key, $value) = each($header)) {
$this->DBFData[$value[0]] = (isset($tmp[$value[0]])) ? $tmp[$value[0]] : "";
}
}
function _loadHeaders() {
$this->recordNumber = loadData("N", fread($this->SHPFile, 4));
$tmp = loadData("N", fread($this->SHPFile, 4)); //We read the length of the record
$this->shapeType = loadData("V", fread($this->SHPFile, 4));
}
function _saveHeaders() {
fwrite($this->SHPFile, pack("N", $this->recordNumber));
fwrite($this->SHPFile, pack("N", $this->getContentLength()));
fwrite($this->SHPFile, pack("V", $this->shapeType));
}
function _loadPoint() {
$data = array();
$data["x"] = loadData("d", fread($this->SHPFile, 8));
$data["y"] = loadData("d", fread($this->SHPFile, 8));
return $data;
}
function _savePoint($data) {
fwrite($this->SHPFile, packDouble($data["x"]));
fwrite($this->SHPFile, packDouble($data["y"]));
}
function _loadNullRecord() {
$this->SHPData = array();
}
function _saveNullRecord() {
//Don't save anything
}
function _loadPointRecord() {
$this->SHPData = $this->_loadPoint();
}
function _savePointRecord() {
$this->_savePoint($this->SHPData);
}
function _loadMultiPointRecord() {
$this->SHPData = array();
$this->SHPData["xmin"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["ymin"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["xmax"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["ymax"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["numpoints"] = loadData("V", fread($this->SHPFile, 4));
for ($i = 0; $i <= $this->SHPData["numpoints"]; $i++) {
$this->SHPData["points"][] = $this->_loadPoint();
}
}
function _saveMultiPointRecord() {
fwrite($this->SHPFile, pack("dddd", $this->SHPData["xmin"], $this->SHPData["ymin"], $this->SHPData["xmax"], $this->SHPData["ymax"]));
fwrite($this->SHPFile, pack("V", $this->SHPData["numpoints"]));
for ($i = 0; $i <= $this->SHPData["numpoints"]; $i++) {
$this->_savePoint($this->SHPData["points"][$i]);
}
}
function _loadPolyLineRecord() {
$this->SHPData = array();
$this->SHPData["xmin"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["ymin"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["xmax"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["ymax"] = loadData("d", fread($this->SHPFile, 8));
$this->SHPData["numparts"] = loadData("V", fread($this->SHPFile, 4));
$this->SHPData["numpoints"] = loadData("V", fread($this->SHPFile, 4));
for ($i = 0; $i < $this->SHPData["numparts"]; $i++) {
$this->SHPData["parts"][$i] = loadData("V", fread($this->SHPFile, 4));
}
$firstIndex = ftell($this->SHPFile);
$readPoints = 0;
reset($this->SHPData["parts"]);
while (list($partIndex, $partData) = each($this->SHPData["parts"])) {
if (!isset($this->SHPData["parts"][$partIndex]["points"]) || !is_array($this->SHPData["parts"][$partIndex]["points"])) {
$this->SHPData["parts"][$partIndex] = array();
$this->SHPData["parts"][$partIndex]["points"] = array();
}
while (!in_array($readPoints, $this->SHPData["parts"]) && ($readPoints < ($this->SHPData["numpoints"])) && !feof($this->SHPFile)) {
$this->SHPData["parts"][$partIndex]["points"][] = $this->_loadPoint();
$readPoints++;
}
}
fseek($this->SHPFile, $firstIndex + ($readPoints*16));
}
function _savePolyLineRecord() {
fwrite($this->SHPFile, pack("dddd", $this->SHPData["xmin"], $this->SHPData["ymin"], $this->SHPData["xmax"], $this->SHPData["ymax"]));
fwrite($this->SHPFile, pack("VV", $this->SHPData["numparts"], $this->SHPData["numpoints"]));
for ($i = 0; $i < $this->SHPData["numparts"]; $i++) {
fwrite($this->SHPFile, pack("V", count($this->SHPData["parts"][$i])));
}
foreach ($this->SHPData["parts"] as $partData){
reset($partData["points"]);
while (list($pointIndex, $pointData) = each($partData["points"])) {
$this->_savePoint($pointData);
}
}
}
function _loadPolygonRecord() {
$this->_loadPolyLineRecord();
}
function _savePolygonRecord() {
$this->_savePolyLineRecord();
}
function addPoint($point, $partIndex = 0) {
switch ($this->shapeType) {
case 0:
//Don't add anything
break;
case 1:
//Substitutes the value of the current point
$this->SHPData = $point;
break;
case 3:
case 5:
//Adds a new point to the selected part
if (!isset($this->SHPData["xmin"]) || ($this->SHPData["xmin"] > $point["x"])) $this->SHPData["xmin"] = $point["x"];
if (!isset($this->SHPData["ymin"]) || ($this->SHPData["ymin"] > $point["y"])) $this->SHPData["ymin"] = $point["y"];
if (!isset($this->SHPData["xmax"]) || ($this->SHPData["xmax"] < $point["x"])) $this->SHPData["xmax"] = $point["x"];
if (!isset($this->SHPData["ymax"]) || ($this->SHPData["ymax"] < $point["y"])) $this->SHPData["ymax"] = $point["y"];
$this->SHPData["parts"][$partIndex]["points"][] = $point;
$this->SHPData["numparts"] = count($this->SHPData["parts"]);
$this->SHPData["numpoints"]++;
break;
case 8:
//Adds a new point
if (!isset($this->SHPData["xmin"]) || ($this->SHPData["xmin"] > $point["x"])) $this->SHPData["xmin"] = $point["x"];
if (!isset($this->SHPData["ymin"]) || ($this->SHPData["ymin"] > $point["y"])) $this->SHPData["ymin"] = $point["y"];
if (!isset($this->SHPData["xmax"]) || ($this->SHPData["xmax"] < $point["x"])) $this->SHPData["xmax"] = $point["x"];
if (!isset($this->SHPData["ymax"]) || ($this->SHPData["ymax"] < $point["y"])) $this->SHPData["ymax"] = $point["y"];
$this->SHPData["points"][] = $point;
$this->SHPData["numpoints"]++;
break;
default:
$this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
break;
}
}
function deletePoint($pointIndex = 0, $partIndex = 0) {
switch ($this->shapeType) {
case 0:
//Don't delete anything
break;
case 1:
//Sets the value of the point to zero
$this->SHPData["x"] = 0.0;
$this->SHPData["y"] = 0.0;
break;
case 3:
case 5:
//Deletes the point from the selected part, if exists
if (isset($this->SHPData["parts"][$partIndex]) && isset($this->SHPData["parts"][$partIndex]["points"][$pointIndex])) {
for ($i = $pointIndex; $i < (count($this->SHPData["parts"][$partIndex]["points"]) - 1); $i++) {
$this->SHPData["parts"][$partIndex]["points"][$i] = $this->SHPData["parts"][$partIndex]["points"][$i + 1];
}
unset($this->SHPData["parts"][$partIndex]["points"][count($this->SHPData["parts"][$partIndex]["points"]) - 1]);
$this->SHPData["numparts"] = count($this->SHPData["parts"]);
$this->SHPData["numpoints"]--;
}
break;
case 8:
//Deletes the point, if exists
if (isset($this->SHPData["points"][$pointIndex])) {
for ($i = $pointIndex; $i < (count($this->SHPData["points"]) - 1); $i++) {
$this->SHPData["points"][$i] = $this->SHPData["points"][$i + 1];
}
unset($this->SHPData["points"][count($this->SHPData["points"]) - 1]);
$this->SHPData["numpoints"]--;
}
break;
default:
$this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
break;
}
}
function getContentLength() {
switch ($this->shapeType) {
case 0:
$result = 0;
break;
case 1:
$result = 10;
break;
case 3:
case 5:
$result = 22 + 2*count($this->SHPData["parts"]);
for ($i = 0; $i < count($this->SHPData["parts"]); $i++) {
$result += 8*count($this->SHPData["parts"][$i]["points"]);
}
break;
case 8:
$result = 20 + 8*count($this->SHPData["points"]);
break;
default:
$result = false;
$this->setError(sprintf("The Shape Type '%s' is not supported.", $this->shapeType));
break;
}
return $result;
}
function _loadDBFData() {
$this->DBFData = @dbase_get_record_with_names($this->DBFFile, $this->recordNumber);
unset($this->DBFData["deleted"]);
}
function _saveDBFData() {
unset($this->DBFData["deleted"]);
if ($this->recordNumber <= dbase_numrecords($this->DBFFile)) {
if (!dbase_replace_record($this->DBFFile, array_values($this->DBFData), $this->recordNumber)) {
$this->setError("I wasn't possible to update the information in the DBF file.");
}
} else {
if (!dbase_add_record($this->DBFFile, array_values($this->DBFData))) {
$this->setError("I wasn't possible to add the information to the DBF file.");
}
}
}
function setError($error) {
$this->lastError = $error;
return false;
}
}

View File

@ -0,0 +1,289 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Set of functions used with the bookmark feature
*
* @package PhpMyAdmin
*/
/**
* Defines the bookmark parameters for the current user
*
* @return array the bookmark parameters for the current user
* @access public
*/
function PMA_Bookmark_getParams()
{
static $cfgBookmark = null;
if (null !== $cfgBookmark) {
return $cfgBookmark;
}
$cfgRelation = PMA_getRelationsParam();
if ($cfgRelation['bookmarkwork']) {
$cfgBookmark = array(
'user' => $GLOBALS['cfg']['Server']['user'],
'db' => $cfgRelation['db'],
'table' => $cfgRelation['bookmark'],
);
} else {
$cfgBookmark = false;
}
return $cfgBookmark;
} // end of the 'PMA_Bookmark_getParams()' function
/**
* Gets the list of bookmarks defined for the current database
*
* @param string|bool $db the current database name or false
*
* @return array the bookmarks list (key as index, label as value),
* or if param is empty, function will give more information,
* array will be unindexed,
* each struct: [db, id, label, shared, query]
*
* @access public
*
* @global resource $controllink the controluser db connection handle
*/
function PMA_Bookmark_getList($db = false)
{
global $controllink;
$cfgBookmark = PMA_Bookmark_getParams();
if (empty($cfgBookmark)) {
return array();
}
if ($db !== false) {
$query = 'SELECT query, label, id FROM ' . PMA\libraries\Util::backquote(
$cfgBookmark['db']
) . '.' . PMA\libraries\Util::backquote($cfgBookmark['table'])
. ' WHERE dbase = \'' . $GLOBALS['dbi']->escapeString($db) . '\''
. ' AND user = \'' . $GLOBALS['dbi']->escapeString($cfgBookmark['user'])
. '\''
. ' ORDER BY label';
$per_user = $GLOBALS['dbi']->fetchResult(
$query,
'id',
null,
$controllink,
PMA\libraries\DatabaseInterface::QUERY_STORE
);
$query = 'SELECT query, label, id FROM ' . PMA\libraries\Util::backquote(
$cfgBookmark['db']
) . '.' . PMA\libraries\Util::backquote($cfgBookmark['table'])
. ' WHERE dbase = \'' . $GLOBALS['dbi']->escapeString($db) . '\''
. ' AND user = \'\''
. ' ORDER BY label';
$global = $GLOBALS['dbi']->fetchResult(
$query,
'id',
null,
$controllink,
PMA\libraries\DatabaseInterface::QUERY_STORE
);
foreach ($global as $key => $val) {
$global[$key]['label'] = $val['label'] . ' (' . __('shared') . ')';
}
$ret = $global + $per_user;
asort($ret);
} else {
$query = "SELECT `label`, `id`, `query`, `dbase` AS `db`,"
. " IF (`user` = '', true, false) AS `shared`"
. " FROM " . PMA\libraries\Util::backquote($cfgBookmark['db'])
. "." . PMA\libraries\Util::backquote($cfgBookmark['table'])
. " WHERE `user` = '' OR"
. " `user` = '" . $GLOBALS['dbi']->escapeString($cfgBookmark['user'])
. "'";
$ret = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
$controllink,
PMA\libraries\DatabaseInterface::QUERY_STORE
);
}
return $ret;
} // end of the 'PMA_Bookmark_getList()' function
/**
* Gets the sql command from a bookmark
*
* @param string $db the current database name
* @param mixed $id the id of the bookmark to get
* @param string $id_field which field to look up the $id
* @param boolean $action_bookmark_all true: get all bookmarks regardless
* of the owning user
* @param boolean $exact_user_match whether to ignore bookmarks with no user
*
* @return string the sql query
*
* @access public
*
* @global resource $controllink the controluser db connection handle
*
*/
function PMA_Bookmark_get($db, $id, $id_field = 'id', $action_bookmark_all = false,
$exact_user_match = false
) {
global $controllink;
$cfgBookmark = PMA_Bookmark_getParams();
if (empty($cfgBookmark)) {
return '';
}
$query = 'SELECT query FROM ' . PMA\libraries\Util::backquote($cfgBookmark['db'])
. '.' . PMA\libraries\Util::backquote($cfgBookmark['table'])
. ' WHERE dbase = \'' . $GLOBALS['dbi']->escapeString($db) . '\'';
if (! $action_bookmark_all) {
$query .= ' AND (user = \''
. $GLOBALS['dbi']->escapeString($cfgBookmark['user']) . '\'';
if (! $exact_user_match) {
$query .= ' OR user = \'\'';
}
$query .= ')';
}
$query .= ' AND ' . PMA\libraries\Util::backquote($id_field) . ' = ' . $id;
return $GLOBALS['dbi']->fetchValue($query, 0, 0, $controllink);
} // end of the 'PMA_Bookmark_get()' function
/**
* Adds a bookmark
*
* @param array $bkm_fields the properties of the bookmark to add; here,
* $bkm_fields['bkm_sql_query'] is urlencoded
* @param boolean $all_users whether to make the bookmark available for all users
*
* @return boolean whether the INSERT succeeds or not
*
* @access public
*
* @global resource $controllink the controluser db connection handle
*/
function PMA_Bookmark_save($bkm_fields, $all_users = false)
{
global $controllink;
$cfgBookmark = PMA_Bookmark_getParams();
if (!(isset($bkm_fields['bkm_sql_query']) && isset($bkm_fields['bkm_label'])
&& mb_strlen($bkm_fields['bkm_sql_query']) > 0
&& mb_strlen($bkm_fields['bkm_label']) > 0)
) {
return false;
}
$query = 'INSERT INTO ' . PMA\libraries\Util::backquote($cfgBookmark['db'])
. '.' . PMA\libraries\Util::backquote($cfgBookmark['table'])
. ' (id, dbase, user, query, label)'
. ' VALUES (NULL, \''
. $GLOBALS['dbi']->escapeString($bkm_fields['bkm_database']) . '\', '
. '\''
. ($all_users
? ''
: $GLOBALS['dbi']->escapeString(
$bkm_fields['bkm_user']
))
. '\', '
. '\''
. $GLOBALS['dbi']->escapeString($bkm_fields['bkm_sql_query'])
. '\', '
. '\'' . $GLOBALS['dbi']->escapeString($bkm_fields['bkm_label']) . '\')';
return $GLOBALS['dbi']->query($query, $controllink);
} // end of the 'PMA_Bookmark_save()' function
/**
* Deletes a bookmark
*
* @param integer $id the id of the bookmark to delete
*
* @return bool true if successful
*
* @access public
*
* @global resource $controllink the controluser db connection handle
*/
function PMA_Bookmark_delete($id)
{
global $controllink;
$cfgBookmark = PMA_Bookmark_getParams();
if (empty($cfgBookmark)) {
return false;
}
$query = 'DELETE FROM ' . PMA\libraries\Util::backquote($cfgBookmark['db'])
. '.' . PMA\libraries\Util::backquote($cfgBookmark['table'])
. ' WHERE (user = \''
. $GLOBALS['dbi']->escapeString($cfgBookmark['user']) . '\''
. ' OR user = \'\')'
. ' AND id = ' . $id;
return $GLOBALS['dbi']->tryQuery($query, $controllink);
} // end of the 'PMA_Bookmark_delete()' function
/**
* Returns the number of variables in a bookmark
*
* @param string $query bookmarked query
*
* @return number number of variables
*/
function PMA_Bookmark_getVariableCount($query)
{
$matches = array();
preg_match_all("/\[VARIABLE[0-9]*\]/", $query, $matches, PREG_SET_ORDER);
return count($matches);
}
/**
* Replace the placeholders in the bookmark query with variables
*
* @param string $query bookmarked query
*
* @return string query with variables applied
*/
function PMA_Bookmark_applyVariables($query)
{
// remove comments that encloses a variable placeholder
$query = preg_replace(
'|/\*(.*\[VARIABLE[0-9]*\].*)\*/|imsU',
'${1}',
$query
);
// replace variable placeholders with values
$number_of_variables = PMA_Bookmark_getVariableCount($query);
for ($i = 1; $i <= $number_of_variables; $i++) {
$var = '';
if (! empty($_REQUEST['bookmark_variable'][$i])) {
$var = $GLOBALS['dbi']->escapeString(
$_REQUEST['bookmark_variable'][$i]
);
}
$query = str_replace('[VARIABLE' . $i . ']', $var, $query);
// backward compatibility
if ($i == 1) {
$query = str_replace('[VARIABLE]', $var, $query);
}
}
return $query;
}

View File

@ -0,0 +1,345 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Contains functions used by browse_foreigners.php
*
* @package PhpMyAdmin
*/
/**
* Function to get html for one relational key
*
* @param integer $horizontal_count the current horizontal count
* @param string $header table header
* @param boolean $odd_row for the row background color
* @param array $keys all the keys
* @param integer $indexByKeyname index by keyname
* @param array $descriptions descriptions
* @param integer $indexByDescription index by description
* @param string $current_value current value on the edit form
*
* @return string $html the generated html
*/
function PMA_getHtmlForOneKey($horizontal_count, $header, $odd_row, $keys,
$indexByKeyname, $descriptions, $indexByDescription, $current_value
) {
$horizontal_count++;
$output = '';
// whether the key name corresponds to the selected value in the form
$rightKeynameIsSelected = false;
$leftKeynameIsSelected = false;
if ($GLOBALS['cfg']['RepeatCells'] > 0
&& $horizontal_count > $GLOBALS['cfg']['RepeatCells']
) {
$output .= $header;
$horizontal_count = 0;
$odd_row = true;
}
// key names and descriptions for the left section,
// sorted by key names
$leftKeyname = $keys[$indexByKeyname];
list(
$leftDescription,
$leftDescriptionTitle
) = PMA_getDescriptionAndTitle($descriptions[$indexByKeyname]);
// key names and descriptions for the right section,
// sorted by descriptions
$rightKeyname = $keys[$indexByDescription];
list(
$rightDescription,
$rightDescriptionTitle
) = PMA_getDescriptionAndTitle($descriptions[$indexByDescription]);
$indexByDescription++;
if (! empty($current_value)) {
$rightKeynameIsSelected = $rightKeyname == $current_value;
$leftKeynameIsSelected = $leftKeyname == $current_value;
}
$output .= '<tr class="noclick ' . ($odd_row ? 'odd' : 'even') . '">';
$odd_row = ! $odd_row;
$output .= PMA_getHtmlForColumnElement(
'class="nowrap"', $leftKeynameIsSelected,
$leftKeyname, $leftDescription,
$leftDescriptionTitle
);
$output .= PMA_getHtmlForColumnElement(
'', $leftKeynameIsSelected, $leftKeyname,
$leftDescription, $leftDescriptionTitle
);
$output .= '<td width="20%">'
. '<img src="' . $GLOBALS['pmaThemeImage'] . 'spacer.png" alt=""'
. ' width="1" height="1" /></td>';
$output .= PMA_getHtmlForColumnElement(
'', $rightKeynameIsSelected, $rightKeyname,
$rightDescription, $rightDescriptionTitle
);
$output .= PMA_getHtmlForColumnElement(
'class="nowrap"', $rightKeynameIsSelected,
$rightKeyname, $rightDescription,
$rightDescriptionTitle
);
$output .= '</tr>';
return array($output, $horizontal_count, $odd_row, $indexByDescription);
}
/**
* Function to get html for relational field selection
*
* @param string $db current database
* @param string $table current table
* @param string $field field
* @param array $foreignData foreign column data
* @param string $fieldkey field key
* @param string $current_value current columns's value
*
* @return string
*/
function PMA_getHtmlForRelationalFieldSelection($db, $table, $field, $foreignData,
$fieldkey, $current_value
) {
$gotopage = PMA_getHtmlForGotoPage($foreignData);
$showall = PMA_getHtmlForShowAll($foreignData);
$output = '<form class="ajax" '
. 'id="browse_foreign_form" name="browse_foreign_from" '
. 'action="browse_foreigners.php" method="post">'
. '<fieldset>'
. PMA_URL_getHiddenInputs($db, $table)
. '<input type="hidden" name="field" value="' . htmlspecialchars($field)
. '" />'
. '<input type="hidden" name="fieldkey" value="'
. (isset($fieldkey) ? htmlspecialchars($fieldkey) : '') . '" />';
if (isset($_REQUEST['rownumber'])) {
$output .= '<input type="hidden" name="rownumber" value="'
. htmlspecialchars($_REQUEST['rownumber']) . '" />';
}
$filter_value = (isset($_REQUEST['foreign_filter'])
? htmlspecialchars($_REQUEST['foreign_filter'])
: '');
$output .= '<span class="formelement">'
. '<label for="input_foreign_filter">' . __('Search:') . '</label>'
. '<input type="text" name="foreign_filter" '
. 'id="input_foreign_filter" '
. 'value="' . $filter_value . '" data-old="' . $filter_value . '" '
. '/>'
. '<input type="submit" name="submit_foreign_filter" value="'
. __('Go') . '" />'
. '</span>'
. '<span class="formelement">' . $gotopage . '</span>'
. '<span class="formelement">' . $showall . '</span>'
. '</fieldset>'
. '</form>';
$output .= '<table width="100%" id="browse_foreign_table">';
if (!is_array($foreignData['disp_row'])) {
$output .= '</tbody>'
. '</table>';
return $output;
}
$header = '<tr>
<th>' . __('Keyname') . '</th>
<th>' . __('Description') . '</th>
<td width="20%"></td>
<th>' . __('Description') . '</th>
<th>' . __('Keyname') . '</th>
</tr>';
$output .= '<thead>' . $header . '</thead>' . "\n"
. '<tfoot>' . $header . '</tfoot>' . "\n"
. '<tbody>' . "\n";
$descriptions = array();
$keys = array();
foreach ($foreignData['disp_row'] as $relrow) {
if ($foreignData['foreign_display'] != false) {
$descriptions[] = $relrow[$foreignData['foreign_display']];
} else {
$descriptions[] = '';
}
$keys[] = $relrow[$foreignData['foreign_field']];
}
asort($keys);
$horizontal_count = 0;
$odd_row = true;
$indexByDescription = 0;
foreach ($keys as $indexByKeyname => $value) {
list(
$html,
$horizontal_count,
$odd_row,
$indexByDescription
) = PMA_getHtmlForOneKey(
$horizontal_count, $header, $odd_row, $keys, $indexByKeyname,
$descriptions, $indexByDescription, $current_value
);
$output .= $html;
}
$output .= '</tbody>'
. '</table>';
return $output;
}
/**
* Get the description (possibly truncated) and the title
*
* @param string $description the key name's description
*
* @return array the new description and title
*/
function PMA_getDescriptionAndTitle($description)
{
$limitChars = $GLOBALS['cfg']['LimitChars'];
if (mb_strlen($description) <= $limitChars) {
$description = htmlspecialchars(
$description
);
$descriptionTitle = '';
} else {
$descriptionTitle = htmlspecialchars(
$description
);
$description = htmlspecialchars(
mb_substr(
$description, 0, $limitChars
)
. '...'
);
}
return array($description, $descriptionTitle);
}
/**
* Function to get html for each column element
*
* @param string $cssClass class="nowrap" or ''
* @param bool $isSelected whether current equals form's value
* @param string $keyname current key
* @param string $description current value
* @param string $title current title
*
* @return string
*/
function PMA_getHtmlForColumnElement($cssClass, $isSelected, $keyname,
$description, $title
) {
$keyname = htmlspecialchars($keyname);
$output = '<td';
if (! empty($cssClass)) {
$output .= ' ' . $cssClass;
}
$output .= '>'
. ($isSelected ? '<strong>' : '')
. '<a class="foreign_value" data-key="' . $keyname . '" '
. 'href="#" title="' . __('Use this value')
. ($title != ''
? ': ' . $title
: '')
. '">';
if ($cssClass !== '') {
$output .= $keyname;
} else {
$output .= $description;
}
$output .= '</a>' . ($isSelected ? '</strong>' : '') . '</td>';
return $output;
}
/**
* Function to get html for show all case
*
* @param array $foreignData foreign data
*
* @return string
*/
function PMA_getHtmlForShowAll($foreignData)
{
$showall = '';
if (is_array($foreignData['disp_row'])) {
if ($GLOBALS['cfg']['ShowAll']
&& ($foreignData['the_total'] > $GLOBALS['cfg']['MaxRows'])
) {
$showall = '<input type="submit" id="foreign_showAll" '
. 'name="foreign_showAll" '
. 'value="' . __('Show all') . '" />';
}
}
return $showall;
}
/**
* Function to get html for the goto page option
*
* @param array $foreignData foreign data
*
* @return string
*/
function PMA_getHtmlForGotoPage($foreignData)
{
$gotopage = '';
isset($_REQUEST['pos']) ? $pos = $_REQUEST['pos'] : $pos = 0;
if (!is_array($foreignData['disp_row'])) {
return $gotopage;
}
$session_max_rows = $GLOBALS['cfg']['MaxRows'];
$pageNow = @floor($pos / $session_max_rows) + 1;
$nbTotalPage = @ceil($foreignData['the_total'] / $session_max_rows);
if ($foreignData['the_total'] > $GLOBALS['cfg']['MaxRows']) {
$gotopage = PMA\libraries\Util::pageselector(
'pos',
$session_max_rows,
$pageNow,
$nbTotalPage,
200,
5,
5,
20,
10,
__('Page number:')
);
}
return $gotopage;
}
/**
* Function to get foreign limit
*
* @param string $foreign_showAll foreign navigation
*
* @return string
*/
function PMA_getForeignLimit($foreign_showAll)
{
if (isset($foreign_showAll) && $foreign_showAll == __('Show all')) {
return null;
}
isset($_REQUEST['pos']) ? $pos = $_REQUEST['pos'] : $pos = 0;
return 'LIMIT ' . $pos . ', ' . intval($GLOBALS['cfg']['MaxRows']) . ' ';
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Charset conversion functions.
*
* @package PhpMyAdmin
*/
if (! defined('PHPMYADMIN')) {
exit;
}
define('PMA_CHARSET_NONE', 0);
define('PMA_CHARSET_ICONV', 1);
define('PMA_CHARSET_RECODE', 2);
define('PMA_CHARSET_ICONV_AIX', 3);
define('PMA_CHARSET_MB', 4);
if (! isset($GLOBALS['cfg']['RecodingEngine'])) {
$GLOBALS['cfg']['RecodingEngine'] = '';
}
// Finally detect which function we will use:
if ($GLOBALS['cfg']['RecodingEngine'] == 'iconv') {
if (@function_exists('iconv')) {
$PMA_recoding_engine = PMA_getIconvRecodingEngine();
} else {
$PMA_recoding_engine = PMA_CHARSET_NONE;
PMA_warnMissingExtension('iconv');
}
} elseif ($GLOBALS['cfg']['RecodingEngine'] == 'recode') {
if (@function_exists('recode_string')) {
$PMA_recoding_engine = PMA_CHARSET_RECODE;
} else {
$PMA_recoding_engine = PMA_CHARSET_NONE;
PMA_warnMissingExtension('recode');
}
} elseif ($GLOBALS['cfg']['RecodingEngine'] == 'mb') {
if (@function_exists('mb_convert_encoding')) {
$PMA_recoding_engine = PMA_CHARSET_MB;
} else {
$PMA_recoding_engine = PMA_CHARSET_NONE;
PMA_warnMissingExtension('mbstring');
}
} elseif ($GLOBALS['cfg']['RecodingEngine'] == 'auto') {
/*
* We also need to verify iconv works, see
* https://github.com/phpmyadmin/phpmyadmin/issues/11787/
* and
* https://bugs.php.net/bug.php?id=44096
*/
if (@function_exists('iconv') && @iconv_strlen('', 'cp1250') !== false) {
$PMA_recoding_engine = PMA_getIconvRecodingEngine();
} elseif (@function_exists('recode_string')) {
$PMA_recoding_engine = PMA_CHARSET_RECODE;
} elseif (@function_exists('mb_convert_encoding')) {
$PMA_recoding_engine = PMA_CHARSET_MB;
} else {
$PMA_recoding_engine = PMA_CHARSET_NONE;
}
} else {
$PMA_recoding_engine = PMA_CHARSET_NONE;
}
/* Load AIX iconv wrapper if needed */
if ($PMA_recoding_engine == PMA_CHARSET_ICONV_AIX) {
include_once './libraries/iconv_wrapper.lib.php';
}
/**
* Determines the correct recoding engine to use
*
* @return int $PMA_recoding_engine
*
* @access public
*
*/
function PMA_getIconvRecodingEngine()
{
if ((@stristr(PHP_OS, 'AIX'))
&& (@strcasecmp(ICONV_IMPL, 'unknown') == 0)
&& (@strcasecmp(ICONV_VERSION, 'unknown') == 0)
) {
return PMA_CHARSET_ICONV_AIX;
} else {
return PMA_CHARSET_ICONV;
}
}
/**
* Converts encoding of text according to parameters with detected
* conversion function.
*
* @param string $src_charset source charset
* @param string $dest_charset target charset
* @param string $what what to convert
*
* @return string converted text
*
* @access public
*
*/
function PMA_convertString($src_charset, $dest_charset, $what)
{
if ($src_charset == $dest_charset) {
return $what;
}
switch ($GLOBALS['PMA_recoding_engine']) {
case PMA_CHARSET_RECODE:
return recode_string($src_charset . '..' . $dest_charset, $what);
case PMA_CHARSET_ICONV:
return iconv(
$src_charset, $dest_charset . $GLOBALS['cfg']['IconvExtraParams'], $what
);
case PMA_CHARSET_ICONV_AIX:
return PMA_convertAIXIconv(
$src_charset, $dest_charset . $GLOBALS['cfg']['IconvExtraParams'], $what
);
case PMA_CHARSET_MB:
return mb_convert_encoding(
$what, $dest_charset, $src_charset
);
default:
return $what;
}
} // end of the "PMA_convertString()" function

View File

@ -0,0 +1,316 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Get user's global privileges and some db-specific privileges
*
* @package PhpMyAdmin
*/
if (! defined('PHPMYADMIN')) {
exit;
}
/**
*
*/
$GLOBALS['is_superuser'] = $GLOBALS['dbi']->isSuperuser();
function PMA_getItemsFromShowGrantsRow($row)
{
$db_name_offset = mb_strpos($row, ' ON ') + 4;
$show_grants_dbname = mb_substr(
$row, $db_name_offset,
mb_strpos($row, '.', $db_name_offset) - $db_name_offset
);
$show_grants_dbname = PMA\libraries\Util::unQuote($show_grants_dbname, '`');
$show_grants_str = mb_substr(
$row,
6,
(mb_strpos($row, ' ON ') - 6)
);
// extrac table from GRANT sytax
$tblname_start_offset = mb_strpos($row, '.') + 1;
$tblname_end_offset = mb_strpos($row, ' TO ');
$show_grants_tblname = mb_substr(
$row, $tblname_start_offset,
$tblname_end_offset - $tblname_start_offset
);
$show_grants_tblname = PMA\libraries\Util::unQuote($show_grants_tblname, '`');
return array(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
);
}
/**
* Check if user has required privileges for
* performing 'Adjust privileges' operations
*
* @param string $show_grants_str string containing grants for user
* @param string $show_grants_dbname name of db extracted from grant string
* @param string $show_grants_tblname name of table extracted from grant string
*
* @return void
*/
function PMA_checkRequiredPrivilegesForAdjust(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
) {
// '... ALL PRIVILEGES ON *.* ...' OR '... ALL PRIVILEGES ON `mysql`.* ..'
// OR
// SELECT, INSERT, UPDATE, DELETE .... ON *.* OR `mysql`.*
if ($show_grants_str == 'ALL'
|| $show_grants_str == 'ALL PRIVILEGES'
|| (mb_strpos(
$show_grants_str, 'SELECT, INSERT, UPDATE, DELETE'
) !== false)
) {
if ($show_grants_dbname == '*'
&& $show_grants_tblname == '*'
) {
$GLOBALS['col_priv'] = true;
$GLOBALS['db_priv'] = true;
$GLOBALS['proc_priv'] = true;
$GLOBALS['table_priv'] = true;
if ($show_grants_str == 'ALL PRIVILEGES'
|| $show_grants_str == 'ALL'
) {
$GLOBALS['is_reload_priv'] = true;
}
}
// check for specific tables in `mysql` db
// Ex. '... ALL PRIVILEGES on `mysql`.`columns_priv` .. '
if ($show_grants_dbname == 'mysql') {
switch ($show_grants_tblname) {
case "columns_priv":
$GLOBALS['col_priv'] = true;
break;
case "db":
$GLOBALS['db_priv'] = true;
break;
case "procs_priv":
$GLOBALS['proc_priv'] = true;
break;
case "tables_priv":
$GLOBALS['table_priv'] = true;
break;
case "*":
$GLOBALS['col_priv'] = true;
$GLOBALS['db_priv'] = true;
$GLOBALS['proc_priv'] = true;
$GLOBALS['table_priv'] = true;
break;
default:
}
}
}
}
/**
* sets privilege information extracted from SHOW GRANTS result
*
* Detection for some CREATE privilege.
*
* Since MySQL 4.1.2, we can easily detect current user's grants using $userlink
* (no control user needed) and we don't have to try any other method for
* detection
*
* @todo fix to get really all privileges, not only explicitly defined for this user
* from MySQL manual: (https://dev.mysql.com/doc/refman/5.0/en/show-grants.html)
* SHOW GRANTS displays only the privileges granted explicitly to the named
* account. Other privileges might be available to the account, but they are not
* displayed. For example, if an anonymous account exists, the named account
* might be able to use its privileges, but SHOW GRANTS will not display them.
*
* @return void
*/
function PMA_analyseShowGrant()
{
if (PMA\libraries\Util::cacheExists('is_create_db_priv')) {
$GLOBALS['is_create_db_priv'] = PMA\libraries\Util::cacheGet(
'is_create_db_priv'
);
$GLOBALS['is_reload_priv'] = PMA\libraries\Util::cacheGet(
'is_reload_priv'
);
$GLOBALS['db_to_create'] = PMA\libraries\Util::cacheGet(
'db_to_create'
);
$GLOBALS['dbs_where_create_table_allowed'] = PMA\libraries\Util::cacheGet(
'dbs_where_create_table_allowed'
);
$GLOBALS['dbs_to_test'] = PMA\libraries\Util::cacheGet(
'dbs_to_test'
);
$GLOBALS['db_priv'] = PMA\libraries\Util::cacheGet(
'db_priv'
);
$GLOBALS['col_priv'] = PMA\libraries\Util::cacheGet(
'col_priv'
);
$GLOBALS['table_priv'] = PMA\libraries\Util::cacheGet(
'table_priv'
);
$GLOBALS['proc_priv'] = PMA\libraries\Util::cacheGet(
'proc_priv'
);
return;
}
// defaults
$GLOBALS['is_create_db_priv'] = false;
$GLOBALS['is_reload_priv'] = false;
$GLOBALS['db_to_create'] = '';
$GLOBALS['dbs_where_create_table_allowed'] = array();
$GLOBALS['dbs_to_test'] = $GLOBALS['dbi']->getSystemSchemas();
$GLOBALS['proc_priv'] = false;
$GLOBALS['db_priv'] = false;
$GLOBALS['col_priv'] = false;
$GLOBALS['table_priv'] = false;
$rs_usr = $GLOBALS['dbi']->tryQuery('SHOW GRANTS');
if (! $rs_usr) {
return;
}
$re0 = '(^|(\\\\\\\\)+|[^\\\\])'; // non-escaped wildcards
$re1 = '(^|[^\\\\])(\\\)+'; // escaped wildcards
while ($row = $GLOBALS['dbi']->fetchRow($rs_usr)) {
list(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
) = PMA_getItemsFromShowGrantsRow($row[0]);
if ($show_grants_dbname == '*') {
if ($show_grants_str != 'USAGE') {
$GLOBALS['dbs_to_test'] = false;
}
} elseif ($GLOBALS['dbs_to_test'] !== false) {
$GLOBALS['dbs_to_test'][] = $show_grants_dbname;
}
if (
mb_strpos($show_grants_str,'RELOAD') !== false
) {
$GLOBALS['is_reload_priv'] = true;
}
// check for the required privileges for adjust
PMA_checkRequiredPrivilegesForAdjust(
$show_grants_str,
$show_grants_dbname,
$show_grants_tblname
);
/**
* @todo if we find CREATE VIEW but not CREATE, do not offer
* the create database dialog box
*/
if ($show_grants_str == 'ALL'
|| $show_grants_str == 'ALL PRIVILEGES'
|| $show_grants_str == 'CREATE'
|| strpos($show_grants_str, 'CREATE,') !== false
) {
if ($show_grants_dbname == '*') {
// a global CREATE privilege
$GLOBALS['is_create_db_priv'] = true;
$GLOBALS['is_reload_priv'] = true;
$GLOBALS['db_to_create'] = '';
$GLOBALS['dbs_where_create_table_allowed'][] = '*';
// @todo we should not break here, cause GRANT ALL *.*
// could be revoked by a later rule like GRANT SELECT ON db.*
break;
} else {
// this array may contain wildcards
$GLOBALS['dbs_where_create_table_allowed'][] = $show_grants_dbname;
$dbname_to_test = PMA\libraries\Util::backquote($show_grants_dbname);
if ($GLOBALS['is_create_db_priv']) {
// no need for any more tests if we already know this
continue;
}
// does this db exist?
if ((preg_match('/' . $re0 . '%|_/', $show_grants_dbname)
&& ! preg_match('/\\\\%|\\\\_/', $show_grants_dbname))
|| (! $GLOBALS['dbi']->tryQuery(
'USE ' . preg_replace(
'/' . $re1 . '(%|_)/', '\\1\\3', $dbname_to_test
)
)
&& mb_substr($GLOBALS['dbi']->getError(), 1, 4) != 1044)
) {
/**
* Do not handle the underscore wildcard
* (this case must be rare anyway)
*/
$GLOBALS['db_to_create'] = preg_replace(
'/' . $re0 . '%/', '\\1',
$show_grants_dbname
);
$GLOBALS['db_to_create'] = preg_replace(
'/' . $re1 . '(%|_)/', '\\1\\3',
$GLOBALS['db_to_create']
);
$GLOBALS['is_create_db_priv'] = true;
/**
* @todo collect $GLOBALS['db_to_create'] into an array,
* to display a drop-down in the "Create database" dialog
*/
// we don't break, we want all possible databases
//break;
} // end if
} // end elseif
} // end if
} // end while
$GLOBALS['dbi']->freeResult($rs_usr);
// must also cacheUnset() them in
// libraries/plugins/auth/AuthenticationCookie.php
PMA\libraries\Util::cacheSet('is_create_db_priv', $GLOBALS['is_create_db_priv']);
PMA\libraries\Util::cacheSet('is_reload_priv', $GLOBALS['is_reload_priv']);
PMA\libraries\Util::cacheSet('db_to_create', $GLOBALS['db_to_create']);
PMA\libraries\Util::cacheSet(
'dbs_where_create_table_allowed',
$GLOBALS['dbs_where_create_table_allowed']
);
PMA\libraries\Util::cacheSet('dbs_to_test', $GLOBALS['dbs_to_test']);
PMA\libraries\Util::cacheSet('proc_priv', $GLOBALS['proc_priv']);
PMA\libraries\Util::cacheSet('table_priv', $GLOBALS['table_priv']);
PMA\libraries\Util::cacheSet('col_priv', $GLOBALS['col_priv']);
PMA\libraries\Util::cacheSet('db_priv', $GLOBALS['db_priv']);
} // end function
$user = $GLOBALS['dbi']->fetchValue("SELECT CURRENT_USER();");
if ($user == '@') { // MySQL is started with --skip-grant-tables
$GLOBALS['is_create_db_priv'] = true;
$GLOBALS['is_reload_priv'] = true;
$GLOBALS['db_to_create'] = '';
$GLOBALS['dbs_where_create_table_allowed'] = array('*');
$GLOBALS['dbs_to_test'] = false;
$GLOBALS['db_priv'] = true;
$GLOBALS['col_priv'] = true;
$GLOBALS['table_priv'] = true;
$GLOBALS['proc_priv'] = true;
} else {
PMA_analyseShowGrant();
}

View File

@ -0,0 +1,47 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Functions for cleanup of user input.
*
* @package PhpMyAdmin
*/
/**
* Removes all variables from request except whitelisted ones.
*
* @param string &$whitelist list of variables to allow
*
* @return void
* @access public
*/
function PMA_removeRequestVars(&$whitelist)
{
// do not check only $_REQUEST because it could have been overwritten
// and use type casting because the variables could have become
// strings
$keys = array_keys(
array_merge((array)$_REQUEST, (array)$_GET, (array)$_POST, (array)$_COOKIE)
);
foreach ($keys as $key) {
if (! in_array($key, $whitelist)) {
unset($_REQUEST[$key], $_GET[$key], $_POST[$key]);
continue;
}
// allowed stuff could be compromised so escape it
// we require it to be a string
if (isset($_REQUEST[$key]) && ! is_string($_REQUEST[$key])) {
unset($_REQUEST[$key]);
}
if (isset($_POST[$key]) && ! is_string($_POST[$key])) {
unset($_POST[$key]);
}
if (isset($_COOKIE[$key]) && ! is_string($_COOKIE[$key])) {
unset($_COOKIE[$key]);
}
if (isset($_GET[$key]) && ! is_string($_GET[$key])) {
unset($_GET[$key]);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Database with allowed values for configuration stored in the $cfg array,
* used by setup script and user preferences to generate forms.
*
* @package PhpMyAdmin
*/
if (!defined('PHPMYADMIN')) {
exit;
}
/**
* Value meaning:
* o array - select field, array contains allowed values
* o string - type override
*
* Use normal array, paths won't be expanded
*/
$cfg_db = array();
$cfg_db['Servers'] = array(
1 => array(
'port' => 'integer',
'connect_type' => array('tcp', 'socket'),
'auth_type' => array('config', 'http', 'signon', 'cookie'),
'AllowDeny' => array(
'order' => array('', 'deny,allow', 'allow,deny', 'explicit')
),
'only_db' => 'array'
)
);
$cfg_db['RecodingEngine'] = array('auto', 'iconv', 'recode', 'mb', 'none');
$cfg_db['OBGzip'] = array('auto', true, false);
$cfg_db['MemoryLimit'] = 'short_string';
$cfg_db['NavigationLogoLinkWindow'] = array('main', 'new');
$cfg_db['NavigationTreeDefaultTabTable'] = array(
'structure' => __('Structure'), // fields list
'sql' => __('SQL'), // SQL form
'search' => __('Search'), // search page
'insert' => __('Insert'), // insert row page
'browse' => __('Browse') // browse page
);
$cfg_db['NavigationTreeDefaultTabTable2'] = array(
'' => '', //don't display
'structure' => __('Structure'), // fields list
'sql' => __('SQL'), // SQL form
'search' => __('Search'), // search page
'insert' => __('Insert'), // insert row page
'browse' => __('Browse') // browse page
);
$cfg_db['NavigationTreeDbSeparator'] = 'short_string';
$cfg_db['NavigationTreeTableSeparator'] = 'short_string';
$cfg_db['TableNavigationLinksMode'] = array(
'icons' => __('Icons'),
'text' => __('Text'),
'both' => __('Both')
);
$cfg_db['MaxRows'] = array(25, 50, 100, 250, 500);
$cfg_db['Order'] = array('ASC', 'DESC', 'SMART');
$cfg_db['RowActionLinks'] = array(
'none' => __('Nowhere'),
'left' => __('Left'),
'right' => __('Right'),
'both' => __('Both')
);
$cfg_db['TablePrimaryKeyOrder'] = array(
'NONE' => __('None'),
'ASC' => __('Ascending'),
'DESC' => __('Descending')
);
$cfg_db['ProtectBinary'] = array(false, 'blob', 'noblob', 'all');
$cfg_db['CharEditing'] = array('input', 'textarea');
$cfg_db['TabsMode'] = array(
'icons' => __('Icons'),
'text' => __('Text'),
'both' => __('Both')
);
$cfg_db['PDFDefaultPageSize'] = array(
'A3' => 'A3',
'A4' => 'A4',
'A5' => 'A5',
'letter' => 'letter',
'legal' => 'legal'
);
$cfg_db['ActionLinksMode'] = array(
'icons' => __('Icons'),
'text' => __('Text'),
'both' => __('Both')
);
$cfg_db['GridEditing'] = array(
'click' => __('Click'),
'double-click' => __('Double click'),
'disabled' => __('Disabled'),
);
$cfg_db['RelationalDisplay'] = array(
'K' => __('key'),
'D' => __('display column')
);
$cfg_db['DefaultTabServer'] = array(
// the welcome page (recommended for multiuser setups)
'welcome' => __('Welcome'),
'databases' => __('Databases'), // list of databases
'status' => __('Status'), // runtime information
'variables' => __('Variables'), // MySQL server variables
'privileges' => __('Privileges') // user management
);
$cfg_db['DefaultTabDatabase'] = array(
'structure' => __('Structure'), // tables list
'sql' => __('SQL'), // SQL form
'search' => __('Search'), // search query
'operations' => __('Operations') // operations on database
);
$cfg_db['DefaultTabTable'] = array(
'structure' => __('Structure'), // fields list
'sql' => __('SQL'), // SQL form
'search' => __('Search'), // search page
'insert' => __('Insert'), // insert row page
'browse' => __('Browse') // browse page
);
$cfg_db['InitialSlidersState'] = array(
'open' => __('Open'),
'closed' => __('Closed'),
'disabled' => __('Disabled')
);
$cfg_db['SendErrorReports'] = array(
'ask' => __('Ask before sending error reports'),
'always' => __('Always send error reports'),
'never' => __('Never send error reports')
);
$cfg_db['DefaultForeignKeyChecks'] = array(
'default' => __('Server default'),
'enable' => __('Enable'),
'disable' => __('Disable')
);
$cfg_db['Import']['format'] = array(
'csv', // CSV
'docsql', // DocSQL
'ldi', // CSV using LOAD DATA
'sql' // SQL
);
$cfg_db['Import']['charset'] = array_merge(
array(''),
$GLOBALS['cfg']['AvailableCharsets']
);
$cfg_db['Import']['sql_compatibility']
= $cfg_db['Export']['sql_compatibility'] = array(
'NONE', 'ANSI', 'DB2', 'MAXDB', 'MYSQL323',
'MYSQL40', 'MSSQL', 'ORACLE',
// removed; in MySQL 5.0.33, this produces exports that
// can't be read by POSTGRESQL (see our bug #1596328)
//'POSTGRESQL',
'TRADITIONAL'
);
$cfg_db['Import']['csv_terminated'] = 'short_string';
$cfg_db['Import']['csv_enclosed'] = 'short_string';
$cfg_db['Import']['csv_escaped'] = 'short_string';
$cfg_db['Import']['ldi_terminated'] = 'short_string';
$cfg_db['Import']['ldi_enclosed'] = 'short_string';
$cfg_db['Import']['ldi_escaped'] = 'short_string';
$cfg_db['Import']['ldi_local_option'] = array('auto', true, false);
$cfg_db['Export']['_sod_select'] = array(
'structure' => __('structure'),
'data' => __('data'),
'structure_and_data' => __('structure and data')
);
$cfg_db['Export']['method'] = array(
'quick' => __('Quick - display only the minimal options to configure'),
'custom' => __('Custom - display all possible options to configure'),
'custom-no-form' => __(
'Custom - like above, but without the quick/custom choice'
),
);
$cfg_db['Export']['format'] = array(
'codegen', 'csv', 'excel', 'htmlexcel','htmlword', 'latex', 'ods',
'odt', 'pdf', 'sql', 'texytext', 'xls', 'xml', 'yaml'
);
$cfg_db['Export']['compression'] = array('none', 'zip', 'gzip');
$cfg_db['Export']['charset'] = array_merge(
array(''),
$GLOBALS['cfg']['AvailableCharsets']
);
$cfg_db['Export']['codegen_format'] = array(
'#', 'NHibernate C# DO', 'NHibernate XML'
);
$cfg_db['Export']['csv_separator'] = 'short_string';
$cfg_db['Export']['csv_terminated'] = 'short_string';
$cfg_db['Export']['csv_enclosed'] = 'short_string';
$cfg_db['Export']['csv_escaped'] = 'short_string';
$cfg_db['Export']['csv_null'] = 'short_string';
$cfg_db['Export']['excel_null'] = 'short_string';
$cfg_db['Export']['excel_edition'] = array(
'win' => 'Windows',
'mac_excel2003' => 'Excel 2003 / Macintosh',
'mac_excel2008' => 'Excel 2008 / Macintosh'
);
$cfg_db['Export']['sql_structure_or_data'] = $cfg_db['Export']['_sod_select'];
$cfg_db['Export']['sql_type'] = array('INSERT', 'UPDATE', 'REPLACE');
$cfg_db['Export']['sql_insert_syntax'] = array(
'complete' => __('complete inserts'),
'extended' => __('extended inserts'),
'both' => __('both of the above'),
'none' => __('neither of the above')
);
$cfg_db['Export']['xls_null'] = 'short_string';
$cfg_db['Export']['xlsx_null'] = 'short_string';
$cfg_db['Export']['htmlword_structure_or_data'] = $cfg_db['Export']['_sod_select'];
$cfg_db['Export']['htmlword_null'] = 'short_string';
$cfg_db['Export']['ods_null'] = 'short_string';
$cfg_db['Export']['odt_null'] = 'short_string';
$cfg_db['Export']['odt_structure_or_data'] = $cfg_db['Export']['_sod_select'];
$cfg_db['Export']['texytext_structure_or_data'] = $cfg_db['Export']['_sod_select'];
$cfg_db['Export']['texytext_null'] = 'short_string';
/**
* Default values overrides
* Use only full paths
*/
$cfg_db['_overrides'] = array();
/**
* Basic validator assignments (functions from libraries/config/Validator.php
* and 'validators' object in js/config.js)
* Use only full paths and form ids
*/
$cfg_db['_validators'] = array(
'CharTextareaCols' => 'validatePositiveNumber',
'CharTextareaRows' => 'validatePositiveNumber',
'ExecTimeLimit' => 'validateNonNegativeNumber',
'Export/sql_max_query_size' => 'validatePositiveNumber',
'FirstLevelNavigationItems' => 'validatePositiveNumber',
'ForeignKeyMaxLimit' => 'validatePositiveNumber',
'Import/csv_enclosed' => array(array('validateByRegex', '/^.?$/')),
'Import/csv_escaped' => array(array('validateByRegex', '/^.$/')),
'Import/csv_terminated' => array(array('validateByRegex', '/^.$/')),
'Import/ldi_enclosed' => array(array('validateByRegex', '/^.?$/')),
'Import/ldi_escaped' => array(array('validateByRegex', '/^.$/')),
'Import/ldi_terminated' => array(array('validateByRegex', '/^.$/')),
'Import/skip_queries' => 'validateNonNegativeNumber',
'InsertRows' => 'validatePositiveNumber',
'NumRecentTables' => 'validateNonNegativeNumber',
'NumFavoriteTables' => 'validateNonNegativeNumber',
'LimitChars' => 'validatePositiveNumber',
'LoginCookieValidity' => 'validatePositiveNumber',
'LoginCookieStore' => 'validateNonNegativeNumber',
'MaxDbList' => 'validatePositiveNumber',
'MaxNavigationItems' => 'validatePositiveNumber',
'MaxCharactersInDisplayedSQL' => 'validatePositiveNumber',
'MaxRows' => 'validatePositiveNumber',
'MaxTableList' => 'validatePositiveNumber',
'MemoryLimit' => array(array('validateByRegex', '/^(-1|(\d+(?:[kmg])?))$/i')),
'NavigationTreeTableLevel' => 'validatePositiveNumber',
'QueryHistoryMax' => 'validatePositiveNumber',
'RepeatCells' => 'validateNonNegativeNumber',
'Server' => 'validateServer',
'Server_pmadb' => 'validatePMAStorage',
'Servers/1/port' => 'validatePortNumber',
'Servers/1/hide_db' => 'validateRegex',
'TextareaCols' => 'validatePositiveNumber',
'TextareaRows' => 'validatePositiveNumber',
'TrustedProxies' => 'validateTrustedProxies');
/**
* Additional validators used for user preferences
*/
$cfg_db['_userValidators'] = array(
'MaxDbList' => array(
array('validateUpperBound', 'value:MaxDbList')
),
'MaxTableList' => array(
array('validateUpperBound', 'value:MaxTableList')
),
'QueryHistoryMax' => array(
array('validateUpperBound', 'value:QueryHistoryMax')
)
);

View File

@ -0,0 +1,542 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Config file management
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\config;
use PMA\libraries\Config;
/**
* Config file management class.
* Stores its data in $_SESSION
*
* @package PhpMyAdmin
*/
class ConfigFile
{
/**
* Stores default PMA config from config.default.php
* @var array
*/
private $_defaultCfg;
/**
* Stores allowed values for non-standard fields
* @var array
*/
private $_cfgDb;
/**
* Stores original PMA config, not modified by user preferences
* @var Config
*/
private $_baseCfg;
/**
* Whether we are currently working in PMA Setup context
* @var bool
*/
private $_isInSetup;
/**
* Keys which will be always written to config file
* @var array
*/
private $_persistKeys = array();
/**
* Changes keys while updating config in {@link updateWithGlobalConfig()}
* or reading by {@link getConfig()} or {@link getConfigArray()}
* @var array
*/
private $_cfgUpdateReadMapping = array();
/**
* Key filter for {@link set()}
* @var array|null
*/
private $_setFilter;
/**
* Instance id (key in $_SESSION array, separate for each server -
* ConfigFile{server id})
* @var string
*/
private $_id;
/**
* Result for {@link _flattenArray()}
* @var array|null
*/
private $_flattenArrayResult;
/**
* Constructor
*
* @param array $base_config base configuration read from
* {@link PMA\libraries\Config::$base_config},
* use only when not in PMA Setup
*/
public function __construct(array $base_config = null)
{
// load default config values
$cfg = &$this->_defaultCfg;
include './libraries/config.default.php';
$cfg['fontsize'] = '82%';
// load additional config information
$cfg_db = &$this->_cfgDb;
include './libraries/config.values.php';
// apply default values overrides
if (count($cfg_db['_overrides'])) {
foreach ($cfg_db['_overrides'] as $path => $value) {
PMA_arrayWrite($path, $cfg, $value);
}
}
$this->_baseCfg = $base_config;
$this->_isInSetup = is_null($base_config);
$this->_id = 'ConfigFile' . $GLOBALS['server'];
if (!isset($_SESSION[$this->_id])) {
$_SESSION[$this->_id] = array();
}
}
/**
* Sets names of config options which will be placed in config file even if
* they are set to their default values (use only full paths)
*
* @param array $keys the names of the config options
*
* @return void
*/
public function setPersistKeys(array $keys)
{
// checking key presence is much faster than searching so move values
// to keys
$this->_persistKeys = array_flip($keys);
}
/**
* Returns flipped array set by {@link setPersistKeys()}
*
* @return array
*/
public function getPersistKeysMap()
{
return $this->_persistKeys;
}
/**
* By default ConfigFile allows setting of all configuration keys, use
* this method to set up a filter on {@link set()} method
*
* @param array|null $keys array of allowed keys or null to remove filter
*
* @return void
*/
public function setAllowedKeys($keys)
{
if ($keys === null) {
$this->_setFilter = null;
return;
}
// checking key presence is much faster than searching so move values
// to keys
$this->_setFilter = array_flip($keys);
}
/**
* Sets path mapping for updating config in
* {@link updateWithGlobalConfig()} or reading
* by {@link getConfig()} or {@link getConfigArray()}
*
* @param array $mapping Contains the mapping of "Server/config options"
* to "Server/1/config options"
*
* @return void
*/
public function setCfgUpdateReadMapping(array $mapping)
{
$this->_cfgUpdateReadMapping = $mapping;
}
/**
* Resets configuration data
*
* @return void
*/
public function resetConfigData()
{
$_SESSION[$this->_id] = array();
}
/**
* Sets configuration data (overrides old data)
*
* @param array $cfg Configuration options
*
* @return void
*/
public function setConfigData(array $cfg)
{
$_SESSION[$this->_id] = $cfg;
}
/**
* Sets config value
*
* @param string $path Path
* @param mixed $value Value
* @param string $canonical_path Canonical path
*
* @return void
*/
public function set($path, $value, $canonical_path = null)
{
if ($canonical_path === null) {
$canonical_path = $this->getCanonicalPath($path);
}
// apply key whitelist
if ($this->_setFilter !== null
&& ! isset($this->_setFilter[$canonical_path])
) {
return;
}
// if the path isn't protected it may be removed
if (isset($this->_persistKeys[$canonical_path])) {
PMA_arrayWrite($path, $_SESSION[$this->_id], $value);
return;
}
$default_value = $this->getDefault($canonical_path);
$remove_path = $value === $default_value;
if ($this->_isInSetup) {
// remove if it has a default value or is empty
$remove_path = $remove_path
|| (empty($value) && empty($default_value));
} else {
// get original config values not overwritten by user
// preferences to allow for overwriting options set in
// config.inc.php with default values
$instance_default_value = PMA_arrayRead(
$canonical_path,
$this->_baseCfg
);
// remove if it has a default value and base config (config.inc.php)
// uses default value
$remove_path = $remove_path
&& ($instance_default_value === $default_value);
}
if ($remove_path) {
PMA_arrayRemove($path, $_SESSION[$this->_id]);
return;
}
PMA_arrayWrite($path, $_SESSION[$this->_id], $value);
}
/**
* Flattens multidimensional array, changes indices to paths
* (eg. 'key/subkey').
* Used as array_walk() callback.
*
* @param mixed $value Value
* @param mixed $key Key
* @param mixed $prefix Prefix
*
* @return void
*/
private function _flattenArray($value, $key, $prefix)
{
// no recursion for numeric arrays
if (is_array($value) && !isset($value[0])) {
$prefix .= $key . '/';
array_walk($value, array($this, '_flattenArray'), $prefix);
} else {
$this->_flattenArrayResult[$prefix . $key] = $value;
}
}
/**
* Returns default config in a flattened array
*
* @return array
*/
public function getFlatDefaultConfig()
{
$this->_flattenArrayResult = array();
array_walk($this->_defaultCfg, array($this, '_flattenArray'), '');
$flat_cfg = $this->_flattenArrayResult;
$this->_flattenArrayResult = null;
return $flat_cfg;
}
/**
* Updates config with values read from given array
* (config will contain differences to defaults from config.defaults.php).
*
* @param array $cfg Configuration
*
* @return void
*/
public function updateWithGlobalConfig(array $cfg)
{
// load config array and flatten it
$this->_flattenArrayResult = array();
array_walk($cfg, array($this, '_flattenArray'), '');
$flat_cfg = $this->_flattenArrayResult;
$this->_flattenArrayResult = null;
// save values map for translating a few user preferences paths,
// should be complemented by code reading from generated config
// to perform inverse mapping
foreach ($flat_cfg as $path => $value) {
if (isset($this->_cfgUpdateReadMapping[$path])) {
$path = $this->_cfgUpdateReadMapping[$path];
}
$this->set($path, $value, $path);
}
}
/**
* Returns config value or $default if it's not set
*
* @param string $path Path of config file
* @param mixed $default Default values
*
* @return mixed
*/
public function get($path, $default = null)
{
return PMA_arrayRead($path, $_SESSION[$this->_id], $default);
}
/**
* Returns default config value or $default it it's not set ie. it doesn't
* exist in config.default.php ($cfg) and config.values.php
* ($_cfg_db['_overrides'])
*
* @param string $canonical_path Canonical path
* @param mixed $default Default value
*
* @return mixed
*/
public function getDefault($canonical_path, $default = null)
{
return PMA_arrayRead($canonical_path, $this->_defaultCfg, $default);
}
/**
* Returns config value, if it's not set uses the default one; returns
* $default if the path isn't set and doesn't contain a default value
*
* @param string $path Path
* @param mixed $default Default value
*
* @return mixed
*/
public function getValue($path, $default = null)
{
$v = PMA_arrayRead($path, $_SESSION[$this->_id], null);
if ($v !== null) {
return $v;
}
$path = $this->getCanonicalPath($path);
return $this->getDefault($path, $default);
}
/**
* Returns canonical path
*
* @param string $path Path
*
* @return string
*/
public function getCanonicalPath($path)
{
return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path);
}
/**
* Returns config database entry for $path ($cfg_db in config_info.php)
*
* @param string $path path of the variable in config db
* @param mixed $default default value
*
* @return mixed
*/
public function getDbEntry($path, $default = null)
{
return PMA_arrayRead($path, $this->_cfgDb, $default);
}
/**
* Returns server count
*
* @return int
*/
public function getServerCount()
{
return isset($_SESSION[$this->_id]['Servers'])
? count($_SESSION[$this->_id]['Servers'])
: 0;
}
/**
* Returns server list
*
* @return array|null
*/
public function getServers()
{
return isset($_SESSION[$this->_id]['Servers'])
? $_SESSION[$this->_id]['Servers']
: null;
}
/**
* Returns DSN of given server
*
* @param integer $server server index
*
* @return string
*/
public function getServerDSN($server)
{
if (!isset($_SESSION[$this->_id]['Servers'][$server])) {
return '';
}
$path = 'Servers/' . $server;
$dsn = 'mysqli://';
if ($this->getValue("$path/auth_type") == 'config') {
$dsn .= $this->getValue("$path/user");
if (! $this->getValue("$path/nopassword")
|| ! empty($this->getValue("$path/password"))
) {
$dsn .= ':***';
}
$dsn .= '@';
}
if ($this->getValue("$path/connect_type") == 'tcp') {
$dsn .= $this->getValue("$path/host");
$port = $this->getValue("$path/port");
if ($port) {
$dsn .= ':' . $port;
}
} else {
$dsn .= $this->getValue("$path/socket");
}
return $dsn;
}
/**
* Returns server name
*
* @param int $id server index
*
* @return string
*/
public function getServerName($id)
{
if (!isset($_SESSION[$this->_id]['Servers'][$id])) {
return '';
}
$verbose = $this->get("Servers/$id/verbose");
if (!empty($verbose)) {
return $verbose;
}
$host = $this->get("Servers/$id/host");
return empty($host) ? 'localhost' : $host;
}
/**
* Removes server
*
* @param int $server server index
*
* @return void
*/
public function removeServer($server)
{
if (!isset($_SESSION[$this->_id]['Servers'][$server])) {
return;
}
$last_server = $this->getServerCount();
for ($i = $server; $i < $last_server; $i++) {
$_SESSION[$this->_id]['Servers'][$i]
= $_SESSION[$this->_id]['Servers'][$i + 1];
}
unset($_SESSION[$this->_id]['Servers'][$last_server]);
if (isset($_SESSION[$this->_id]['ServerDefault'])
&& $_SESSION[$this->_id]['ServerDefault'] == $last_server
) {
unset($_SESSION[$this->_id]['ServerDefault']);
}
}
/**
* Returns config file path, relative to phpMyAdmin's root path
*
* @return string
*/
public function getFilePath()
{
return SETUP_CONFIG_FILE;
}
/**
* Returns configuration array (full, multidimensional format)
*
* @return array
*/
public function getConfig()
{
$c = $_SESSION[$this->_id];
foreach ($this->_cfgUpdateReadMapping as $map_to => $map_from) {
// if the key $c exists in $map_to
if (PMA_arrayRead($map_to, $c) !== null) {
PMA_arrayWrite($map_to, $c, PMA_arrayRead($map_from, $c));
PMA_arrayRemove($map_from, $c);
}
}
return $c;
}
/**
* Returns configuration array (flat format)
*
* @return array
*/
public function getConfigArray()
{
$this->_flattenArrayResult = array();
array_walk($_SESSION[$this->_id], array($this, '_flattenArray'), '');
$c = $this->_flattenArrayResult;
$this->_flattenArrayResult = null;
$persistKeys = array_diff(
array_keys($this->_persistKeys),
array_keys($c)
);
foreach ($persistKeys as $k) {
$c[$k] = $this->getDefault($this->getCanonicalPath($k));
}
foreach ($this->_cfgUpdateReadMapping as $map_to => $map_from) {
if (!isset($c[$map_from])) {
continue;
}
$c[$map_to] = $c[$map_from];
unset($c[$map_from]);
}
return $c;
}
}

View File

@ -0,0 +1,231 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form handling code.
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\config;
/**
* Base class for forms, loads default configuration options, checks allowed
* values etc.
*
* @package PhpMyAdmin
*/
class Form
{
/**
* Form name
* @var string
*/
public $name;
/**
* Arbitrary index, doesn't affect class' behavior
* @var int
*/
public $index;
/**
* Form fields (paths), filled by {@link readFormPaths()}, indexed by field name
* @var array
*/
public $fields;
/**
* Stores default values for some fields (eg. pmadb tables)
* @var array
*/
public $default;
/**
* Caches field types, indexed by field names
* @var array
*/
private $_fieldsTypes;
/**
* ConfigFile instance
* @var ConfigFile
*/
private $_configFile;
/**
* Constructor, reads default config values
*
* @param string $form_name Form name
* @param array $form Form data
* @param ConfigFile $cf Config file instance
* @param int $index arbitrary index, stored in Form::$index
*/
public function __construct(
$form_name, array $form, ConfigFile $cf, $index = null
) {
$this->index = $index;
$this->_configFile = $cf;
$this->loadForm($form_name, $form);
}
/**
* Returns type of given option
*
* @param string $option_name path or field name
*
* @return string|null one of: boolean, integer, double, string, select, array
*/
public function getOptionType($option_name)
{
$key = ltrim(
mb_substr(
$option_name,
mb_strrpos($option_name, '/')
),
'/'
);
return isset($this->_fieldsTypes[$key])
? $this->_fieldsTypes[$key]
: null;
}
/**
* Returns allowed values for select fields
*
* @param string $option_path Option path
*
* @return array
*/
public function getOptionValueList($option_path)
{
$value = $this->_configFile->getDbEntry($option_path);
if ($value === null) {
trigger_error("$option_path - select options not defined", E_USER_ERROR);
return array();
}
if (!is_array($value)) {
trigger_error("$option_path - not a static value list", E_USER_ERROR);
return array();
}
// convert array('#', 'a', 'b') to array('a', 'b')
if (isset($value[0]) && $value[0] === '#') {
// remove first element ('#')
array_shift($value);
// $value has keys and value names, return it
return $value;
}
// convert value list array('a', 'b') to array('a' => 'a', 'b' => 'b')
$has_string_keys = false;
$keys = array();
for ($i = 0, $nb = count($value); $i < $nb; $i++) {
if (!isset($value[$i])) {
$has_string_keys = true;
break;
}
$keys[] = is_bool($value[$i]) ? (int)$value[$i] : $value[$i];
}
if (! $has_string_keys) {
$value = array_combine($keys, $value);
}
// $value has keys and value names, return it
return $value;
}
/**
* array_walk callback function, reads path of form fields from
* array (see file comment in setup.forms.php or user_preferences.forms.inc)
*
* @param mixed $value Value
* @param mixed $key Key
* @param mixed $prefix Prefix
*
* @return void
*/
private function _readFormPathsCallback($value, $key, $prefix)
{
static $group_counter = 0;
if (is_array($value)) {
$prefix .= $key . '/';
array_walk($value, array($this, '_readFormPathsCallback'), $prefix);
return;
}
if (!is_int($key)) {
$this->default[$prefix . $key] = $value;
$value = $key;
}
// add unique id to group ends
if ($value == ':group:end') {
$value .= ':' . $group_counter++;
}
$this->fields[] = $prefix . $value;
}
/**
* Reads form paths to {@link $fields}
*
* @param array $form Form
*
* @return void
*/
protected function readFormPaths($form)
{
// flatten form fields' paths and save them to $fields
$this->fields = array();
array_walk($form, array($this, '_readFormPathsCallback'), '');
// $this->fields is an array of the form: [0..n] => 'field path'
// change numeric indexes to contain field names (last part of the path)
$paths = $this->fields;
$this->fields = array();
foreach ($paths as $path) {
$key = ltrim(
mb_substr($path, mb_strrpos($path, '/')),
'/'
);
$this->fields[$key] = $path;
}
// now $this->fields is an array of the form: 'field name' => 'field path'
}
/**
* Reads fields' types to $this->_fieldsTypes
*
* @return void
*/
protected function readTypes()
{
$cf = $this->_configFile;
foreach ($this->fields as $name => $path) {
if (mb_strpos($name, ':group:') === 0) {
$this->_fieldsTypes[$name] = 'group';
continue;
}
$v = $cf->getDbEntry($path);
if ($v !== null) {
$type = is_array($v) ? 'select' : $v;
} else {
$type = gettype($cf->getDefault($path));
}
$this->_fieldsTypes[$name] = $type;
}
}
/**
* Reads form settings and prepares class to work with given subset of
* config file
*
* @param string $form_name Form name
* @param array $form Form
*
* @return void
*/
public function loadForm($form_name, $form)
{
$this->name = $form_name;
$this->readFormPaths($form);
$this->readTypes();
}
}

View File

@ -0,0 +1,887 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form management class, displays and processes forms
*
* Explanation of used terms:
* o work_path - original field path, eg. Servers/4/verbose
* o system_path - work_path modified so that it points to the first server,
* eg. Servers/1/verbose
* o translated_path - work_path modified for HTML field name, a path with
* slashes changed to hyphens, eg. Servers-4-verbose
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\config;
/**
* Core libraries.
*/
use PMA\libraries\Util;
require_once './libraries/js_escape.lib.php';
require_once './libraries/config/FormDisplay.tpl.php';
/**
* Form management class, displays and processes forms
*
* @package PhpMyAdmin
*/
class FormDisplay
{
/**
* ConfigFile instance
* @var ConfigFile
*/
private $_configFile;
/**
* Form list
* @var Form[]
*/
private $_forms = array();
/**
* Stores validation errors, indexed by paths
* [ Form_name ] is an array of form errors
* [path] is a string storing error associated with single field
* @var array
*/
private $_errors = array();
/**
* Paths changed so that they can be used as HTML ids, indexed by paths
* @var array
*/
private $_translatedPaths = array();
/**
* Server paths change indexes so we define maps from current server
* path to the first one, indexed by work path
* @var array
*/
private $_systemPaths = array();
/**
* Language strings which will be sent to PMA_messages JS variable
* Will be looked up in $GLOBALS: str{value} or strSetup{value}
* @var array
*/
private $_jsLangStrings = array();
/**
* Tells whether forms have been validated
* @var bool
*/
private $_isValidated = true;
/**
* Dictionary with user preferences keys
* @var array|null
*/
private $_userprefsKeys;
/**
* Dictionary with disallowed user preferences keys
* @var array
*/
private $_userprefsDisallow;
/**
* Constructor
*
* @param ConfigFile $cf Config file instance
*/
public function __construct(ConfigFile $cf)
{
$this->_jsLangStrings = array(
'error_nan_p' => __('Not a positive number!'),
'error_nan_nneg' => __('Not a non-negative number!'),
'error_incorrect_port' => __('Not a valid port number!'),
'error_invalid_value' => __('Incorrect value!'),
'error_value_lte' => __('Value must be equal or lower than %s!'));
$this->_configFile = $cf;
// initialize validators
Validator::getValidators($this->_configFile);
}
/**
* Returns {@link ConfigFile} associated with this instance
*
* @return ConfigFile
*/
public function getConfigFile()
{
return $this->_configFile;
}
/**
* Registers form in form manager
*
* @param string $form_name Form name
* @param array $form Form data
* @param int $server_id 0 if new server, validation; >= 1 if editing a server
*
* @return void
*/
public function registerForm($form_name, array $form, $server_id = null)
{
$this->_forms[$form_name] = new Form(
$form_name, $form, $this->_configFile, $server_id
);
$this->_isValidated = false;
foreach ($this->_forms[$form_name]->fields as $path) {
$work_path = $server_id === null
? $path
: str_replace('Servers/1/', "Servers/$server_id/", $path);
$this->_systemPaths[$work_path] = $path;
$this->_translatedPaths[$work_path] = str_replace('/', '-', $work_path);
}
}
/**
* Processes forms, returns true on successful save
*
* @param bool $allow_partial_save allows for partial form saving
* on failed validation
* @param bool $check_form_submit whether check for $_POST['submit_save']
*
* @return boolean whether processing was successful
*/
public function process($allow_partial_save = true, $check_form_submit = true)
{
if ($check_form_submit && !isset($_POST['submit_save'])) {
return false;
}
// save forms
if (count($this->_forms) > 0) {
return $this->save(array_keys($this->_forms), $allow_partial_save);
}
return false;
}
/**
* Runs validation for all registered forms
*
* @return void
*/
private function _validate()
{
if ($this->_isValidated) {
return;
}
$paths = array();
$values = array();
foreach ($this->_forms as $form) {
/* @var $form Form */
$paths[] = $form->name;
// collect values and paths
foreach ($form->fields as $path) {
$work_path = array_search($path, $this->_systemPaths);
$values[$path] = $this->_configFile->getValue($work_path);
$paths[] = $path;
}
}
// run validation
$errors = Validator::validate(
$this->_configFile, $paths, $values, false
);
// change error keys from canonical paths to work paths
if (is_array($errors) && count($errors) > 0) {
$this->_errors = array();
foreach ($errors as $path => $error_list) {
$work_path = array_search($path, $this->_systemPaths);
// field error
if (! $work_path) {
// form error, fix path
$work_path = $path;
}
$this->_errors[$work_path] = $error_list;
}
}
$this->_isValidated = true;
}
/**
* Outputs HTML for the forms under the menu tab
*
* @param bool $show_restore_default whether to show "restore default"
* button besides the input field
* @param array &$js_default stores JavaScript code
* to be displayed
* @param array &$js will be updated with javascript code
* @param bool $show_buttons whether show submit and reset button
*
* @return string $htmlOutput
*/
private function _displayForms(
$show_restore_default, array &$js_default, array &$js, $show_buttons
) {
$htmlOutput = '';
$validators = Validator::getValidators($this->_configFile);
foreach ($this->_forms as $form) {
/* @var $form Form */
$form_desc = isset($GLOBALS["strConfigForm_{$form->name}_desc"])
? PMA_lang("Form_{$form->name}_desc")
: '';
$form_errors = isset($this->_errors[$form->name])
? $this->_errors[$form->name] : null;
$htmlOutput .= PMA_displayFieldsetTop(
PMA_lang("Form_$form->name"),
$form_desc,
$form_errors,
array('id' => $form->name)
);
foreach ($form->fields as $field => $path) {
$work_path = array_search($path, $this->_systemPaths);
$translated_path = $this->_translatedPaths[$work_path];
// always true/false for user preferences display
// otherwise null
$userprefs_allow = isset($this->_userprefsKeys[$path])
? !isset($this->_userprefsDisallow[$path])
: null;
// display input
$htmlOutput .= $this->_displayFieldInput(
$form,
$field,
$path,
$work_path,
$translated_path,
$show_restore_default,
$userprefs_allow,
$js_default
);
// register JS validators for this field
if (isset($validators[$path])) {
PMA_addJsValidate($translated_path, $validators[$path], $js);
}
}
$htmlOutput .= PMA_displayFieldsetBottom($show_buttons);
}
return $htmlOutput;
}
/**
* Outputs HTML for forms
*
* @param bool $tabbed_form if true, use a form with tabs
* @param bool $show_restore_default whether show "restore default" button
* besides the input field
* @param bool $show_buttons whether show submit and reset button
* @param string $form_action action attribute for the form
* @param array $hidden_fields array of form hidden fields (key: field
* name)
*
* @return string HTML for forms
*/
public function getDisplay(
$tabbed_form = false,
$show_restore_default = false,
$show_buttons = true,
$form_action = null,
$hidden_fields = null
) {
static $js_lang_sent = false;
$htmlOutput = '';
$js = array();
$js_default = array();
$htmlOutput .= PMA_displayFormTop($form_action, 'post', $hidden_fields);
if ($tabbed_form) {
$tabs = array();
foreach ($this->_forms as $form) {
$tabs[$form->name] = PMA_lang("Form_$form->name");
}
$htmlOutput .= PMA_displayTabsTop($tabs);
}
// validate only when we aren't displaying a "new server" form
$is_new_server = false;
foreach ($this->_forms as $form) {
/* @var $form Form */
if ($form->index === 0) {
$is_new_server = true;
break;
}
}
if (! $is_new_server) {
$this->_validate();
}
// user preferences
$this->_loadUserprefsInfo();
// display forms
$htmlOutput .= $this->_displayForms(
$show_restore_default, $js_default, $js, $show_buttons
);
if ($tabbed_form) {
$htmlOutput .= PMA_displayTabsBottom();
}
$htmlOutput .= PMA_displayFormBottom();
// if not already done, send strings used for validation to JavaScript
if (! $js_lang_sent) {
$js_lang_sent = true;
$js_lang = array();
foreach ($this->_jsLangStrings as $strName => $strValue) {
$js_lang[] = "'$strName': '" . PMA_jsFormat($strValue, false) . '\'';
}
$js[] = "$.extend(PMA_messages, {\n\t"
. implode(",\n\t", $js_lang) . '})';
}
$js[] = "$.extend(defaultValues, {\n\t"
. implode(",\n\t", $js_default) . '})';
$htmlOutput .= PMA_displayJavascript($js);
return $htmlOutput;
}
/**
* Prepares data for input field display and outputs HTML code
*
* @param Form $form Form object
* @param string $field field name as it appears in $form
* @param string $system_path field path, eg. Servers/1/verbose
* @param string $work_path work path, eg. Servers/4/verbose
* @param string $translated_path work path changed so that it can be
* used as XHTML id
* @param bool $show_restore_default whether show "restore default" button
* besides the input field
* @param bool|null $userprefs_allow whether user preferences are enabled
* for this field (null - no support,
* true/false - enabled/disabled)
* @param array &$js_default array which stores JavaScript code
* to be displayed
*
* @return string HTML for input field
*/
private function _displayFieldInput(
Form $form, $field, $system_path, $work_path,
$translated_path, $show_restore_default, $userprefs_allow, array &$js_default
) {
$name = PMA_langName($system_path);
$description = PMA_langName($system_path, 'desc', '');
$value = $this->_configFile->get($work_path);
$value_default = $this->_configFile->getDefault($system_path);
$value_is_default = false;
if ($value === null || $value === $value_default) {
$value = $value_default;
$value_is_default = true;
}
$opts = array(
'doc' => $this->getDocLink($system_path),
'show_restore_default' => $show_restore_default,
'userprefs_allow' => $userprefs_allow,
'userprefs_comment' => PMA_langName($system_path, 'cmt', ''));
if (isset($form->default[$system_path])) {
$opts['setvalue'] = $form->default[$system_path];
}
if (isset($this->_errors[$work_path])) {
$opts['errors'] = $this->_errors[$work_path];
}
$type = '';
switch ($form->getOptionType($field)) {
case 'string':
$type = 'text';
break;
case 'short_string':
$type = 'short_text';
break;
case 'double':
case 'integer':
$type = 'number_text';
break;
case 'boolean':
$type = 'checkbox';
break;
case 'select':
$type = 'select';
$opts['values'] = $form->getOptionValueList($form->fields[$field]);
break;
case 'array':
$type = 'list';
$value = (array) $value;
$value_default = (array) $value_default;
break;
case 'group':
// :group:end is changed to :group:end:{unique id} in Form class
$htmlOutput = '';
if (mb_substr($field, 7, 4) != 'end:') {
$htmlOutput .= PMA_displayGroupHeader(
mb_substr($field, 7)
);
} else {
PMA_displayGroupFooter();
}
return $htmlOutput;
case 'NULL':
trigger_error("Field $system_path has no type", E_USER_WARNING);
return null;
}
// detect password fields
if ($type === 'text'
&& (mb_substr($translated_path, -9) === '-password'
|| mb_substr($translated_path, -4) === 'pass'
|| mb_substr($translated_path, -4) === 'Pass')
) {
$type = 'password';
}
// TrustedProxies requires changes before displaying
if ($system_path == 'TrustedProxies') {
foreach ($value as $ip => &$v) {
if (!preg_match('/^-\d+$/', $ip)) {
$v = $ip . ': ' . $v;
}
}
}
$this->_setComments($system_path, $opts);
// send default value to form's JS
$js_line = '\'' . $translated_path . '\': ';
switch ($type) {
case 'text':
case 'short_text':
case 'number_text':
case 'password':
$js_line .= '\'' . PMA_escapeJsString($value_default) . '\'';
break;
case 'checkbox':
$js_line .= $value_default ? 'true' : 'false';
break;
case 'select':
$value_default_js = is_bool($value_default)
? (int) $value_default
: $value_default;
$js_line .= '[\'' . PMA_escapeJsString($value_default_js) . '\']';
break;
case 'list':
$js_line .= '\'' . PMA_escapeJsString(implode("\n", $value_default))
. '\'';
break;
}
$js_default[] = $js_line;
return PMA_displayInput(
$translated_path, $name, $type, $value,
$description, $value_is_default, $opts
);
}
/**
* Displays errors
*
* @return string HTML for errors
*/
public function displayErrors()
{
$this->_validate();
if (count($this->_errors) == 0) {
return null;
}
$htmlOutput = '';
foreach ($this->_errors as $system_path => $error_list) {
if (isset($this->_systemPaths[$system_path])) {
$path = $this->_systemPaths[$system_path];
$name = PMA_langName($path);
} else {
$name = $GLOBALS["strConfigForm_$system_path"];
}
$htmlOutput .= PMA_displayErrors($name, $error_list);
}
return $htmlOutput;
}
/**
* Reverts erroneous fields to their default values
*
* @return void
*/
public function fixErrors()
{
$this->_validate();
if (count($this->_errors) == 0) {
return;
}
$cf = $this->_configFile;
foreach (array_keys($this->_errors) as $work_path) {
if (!isset($this->_systemPaths[$work_path])) {
continue;
}
$canonical_path = $this->_systemPaths[$work_path];
$cf->set($work_path, $cf->getDefault($canonical_path));
}
}
/**
* Validates select field and casts $value to correct type
*
* @param string &$value Current value
* @param array $allowed List of allowed values
*
* @return bool
*/
private function _validateSelect(&$value, array $allowed)
{
$value_cmp = is_bool($value)
? (int) $value
: $value;
foreach ($allowed as $vk => $v) {
// equality comparison only if both values are numeric or not numeric
// (allows to skip 0 == 'string' equalling to true)
// or identity (for string-string)
if (($vk == $value && !(is_numeric($value_cmp) xor is_numeric($vk)))
|| $vk === $value
) {
// keep boolean value as boolean
if (!is_bool($value)) {
settype($value, gettype($vk));
}
return true;
}
}
return false;
}
/**
* Validates and saves form data to session
*
* @param array|string $forms array of form names
* @param bool $allow_partial_save allows for partial form saving on
* failed validation
*
* @return boolean true on success (no errors and all saved)
*/
public function save($forms, $allow_partial_save = true)
{
$result = true;
$forms = (array) $forms;
$values = array();
$to_save = array();
$is_setup_script = defined('PMA_SETUP');
if ($is_setup_script) {
$this->_loadUserprefsInfo();
}
$this->_errors = array();
foreach ($forms as $form_name) {
/* @var $form Form */
if (isset($this->_forms[$form_name])) {
$form = $this->_forms[$form_name];
} else {
continue;
}
// get current server id
$change_index = $form->index === 0
? $this->_configFile->getServerCount() + 1
: false;
// grab POST values
foreach ($form->fields as $field => $system_path) {
$work_path = array_search($system_path, $this->_systemPaths);
$key = $this->_translatedPaths[$work_path];
$type = $form->getOptionType($field);
// skip groups
if ($type == 'group') {
continue;
}
// ensure the value is set
if (!isset($_POST[$key])) {
// checkboxes aren't set by browsers if they're off
if ($type == 'boolean') {
$_POST[$key] = false;
} else {
$this->_errors[$form->name][] = sprintf(
__('Missing data for %s'),
'<i>' . PMA_langName($system_path) . '</i>'
);
$result = false;
continue;
}
}
// user preferences allow/disallow
if ($is_setup_script
&& isset($this->_userprefsKeys[$system_path])
) {
if (isset($this->_userprefsDisallow[$system_path])
&& isset($_POST[$key . '-userprefs-allow'])
) {
unset($this->_userprefsDisallow[$system_path]);
} else if (!isset($_POST[$key . '-userprefs-allow'])) {
$this->_userprefsDisallow[$system_path] = true;
}
}
// cast variables to correct type
switch ($type) {
case 'double':
$_POST[$key] = Util::requestString($_POST[$key]);
settype($_POST[$key], 'float');
break;
case 'boolean':
case 'integer':
if ($_POST[$key] !== '') {
$_POST[$key] = Util::requestString($_POST[$key]);
settype($_POST[$key], $type);
}
break;
case 'select':
$successfully_validated = $this->_validateSelect(
$_POST[$key],
$form->getOptionValueList($system_path)
);
if (! $successfully_validated) {
$this->_errors[$work_path][] = __('Incorrect value!');
$result = false;
continue;
}
break;
case 'string':
case 'short_string':
$_POST[$key] = Util::requestString($_POST[$key]);
break;
case 'array':
// eliminate empty values and ensure we have an array
$post_values = is_array($_POST[$key])
? $_POST[$key]
: explode("\n", $_POST[$key]);
$_POST[$key] = array();
$this->_fillPostArrayParameters($post_values, $key);
break;
}
// now we have value with proper type
$values[$system_path] = $_POST[$key];
if ($change_index !== false) {
$work_path = str_replace(
"Servers/$form->index/",
"Servers/$change_index/", $work_path
);
}
$to_save[$work_path] = $system_path;
}
}
// save forms
if (!$allow_partial_save && !empty($this->_errors)) {
// don't look for non-critical errors
$this->_validate();
return $result;
}
foreach ($to_save as $work_path => $path) {
// TrustedProxies requires changes before saving
if ($path == 'TrustedProxies') {
$proxies = array();
$i = 0;
foreach ($values[$path] as $value) {
$matches = array();
$match = preg_match(
"/^(.+):(?:[ ]?)(\\w+)$/", $value, $matches
);
if ($match) {
// correct 'IP: HTTP header' pair
$ip = trim($matches[1]);
$proxies[$ip] = trim($matches[2]);
} else {
// save also incorrect values
$proxies["-$i"] = $value;
$i++;
}
}
$values[$path] = $proxies;
}
$this->_configFile->set($work_path, $values[$path], $path);
}
if ($is_setup_script) {
$this->_configFile->set(
'UserprefsDisallow',
array_keys($this->_userprefsDisallow)
);
}
// don't look for non-critical errors
$this->_validate();
return $result;
}
/**
* Tells whether form validation failed
*
* @return boolean
*/
public function hasErrors()
{
return count($this->_errors) > 0;
}
/**
* Returns link to documentation
*
* @param string $path Path to documentation
*
* @return string
*/
public function getDocLink($path)
{
$test = mb_substr($path, 0, 6);
if ($test == 'Import' || $test == 'Export') {
return '';
}
return Util::getDocuLink(
'config',
'cfg_' . $this->_getOptName($path)
);
}
/**
* Changes path so it can be used in URLs
*
* @param string $path Path
*
* @return string
*/
private function _getOptName($path)
{
return str_replace(array('Servers/1/', '/'), array('Servers/', '_'), $path);
}
/**
* Fills out {@link userprefs_keys} and {@link userprefs_disallow}
*
* @return void
*/
private function _loadUserprefsInfo()
{
if ($this->_userprefsKeys !== null) {
return;
}
$this->_userprefsKeys = array_flip(PMA_readUserprefsFieldNames());
// read real config for user preferences display
$userprefs_disallow = defined('PMA_SETUP')
? $this->_configFile->get('UserprefsDisallow', array())
: $GLOBALS['cfg']['UserprefsDisallow'];
$this->_userprefsDisallow = array_flip($userprefs_disallow);
}
/**
* Sets field comments and warnings based on current environment
*
* @param string $system_path Path to settings
* @param array &$opts Chosen options
*
* @return void
*/
private function _setComments($system_path, array &$opts)
{
// RecodingEngine - mark unavailable types
if ($system_path == 'RecodingEngine') {
$comment = '';
if (!function_exists('iconv')) {
$opts['values']['iconv'] .= ' (' . __('unavailable') . ')';
$comment = sprintf(
__('"%s" requires %s extension'), 'iconv', 'iconv'
);
}
if (!function_exists('recode_string')) {
$opts['values']['recode'] .= ' (' . __('unavailable') . ')';
$comment .= ($comment ? ", " : '') . sprintf(
__('"%s" requires %s extension'),
'recode', 'recode'
);
}
if (!function_exists('mb_convert_encoding')) {
$opts['values']['mb'] .= ' (' . __('unavailable') . ')';
$comment .= ($comment ? ", " : '') . sprintf(
__('"%s" requires %s extension'),
'mb', 'mbstring'
);
}
$opts['comment'] = $comment;
$opts['comment_warning'] = true;
}
// ZipDump, GZipDump, BZipDump - check function availability
if ($system_path == 'ZipDump'
|| $system_path == 'GZipDump'
|| $system_path == 'BZipDump'
) {
$comment = '';
$funcs = array(
'ZipDump' => array('zip_open', 'gzcompress'),
'GZipDump' => array('gzopen', 'gzencode'),
'BZipDump' => array('bzopen', 'bzcompress'));
if (!function_exists($funcs[$system_path][0])) {
$comment = sprintf(
__(
'Compressed import will not work due to missing function %s.'
),
$funcs[$system_path][0]
);
}
if (!function_exists($funcs[$system_path][1])) {
$comment .= ($comment ? '; ' : '') . sprintf(
__(
'Compressed export will not work due to missing function %s.'
),
$funcs[$system_path][1]
);
}
$opts['comment'] = $comment;
$opts['comment_warning'] = true;
}
if (!defined('PMA_SETUP')) {
if (($system_path == 'MaxDbList' || $system_path == 'MaxTableList'
|| $system_path == 'QueryHistoryMax')
) {
$opts['comment'] = sprintf(
__('maximum %s'), $GLOBALS['cfg'][$system_path]
);
}
}
}
/**
* Copy items of an array to $_POST variable
*
* @param array $post_values List of parameters
* @param string $key Array key
*
* @return void
*/
private function _fillPostArrayParameters($post_values, $key)
{
foreach ($post_values as $v) {
$v = Util::requestString($v);
if ($v !== '') {
$_POST[$key][] = $v;
}
}
}
}

View File

@ -0,0 +1,526 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form templates
*
* @package PhpMyAdmin
*/
use PMA\libraries\Template;
use PMA\libraries\Util;
/**
* Displays top part of the form
*
* @param string $action default: $_SERVER['REQUEST_URI']
* @param string $method 'post' or 'get'
* @param array $hidden_fields array of form hidden fields (key: field name)
*
* @return string
*/
function PMA_displayFormTop($action = null, $method = 'post', $hidden_fields = null)
{
static $has_check_page_refresh = false;
if ($action === null) {
$action = $_SERVER['REQUEST_URI'];
}
if ($method != 'post') {
$method = 'get';
}
$htmlOutput = '<form method="' . $method . '" action="'
. htmlspecialchars($action) . '" class="config-form disableAjax">';
$htmlOutput .= '<input type="hidden" name="tab_hash" value="" />';
// we do validation on page refresh when browser remembers field values,
// add a field with known value which will be used for checks
if (! $has_check_page_refresh) {
$has_check_page_refresh = true;
$htmlOutput .= '<input type="hidden" name="check_page_refresh" '
. ' id="check_page_refresh" value="" />' . "\n";
}
$htmlOutput .= PMA_URL_getHiddenInputs('', '', 0, 'server') . "\n";
$htmlOutput .= PMA_getHiddenFields((array)$hidden_fields);
return $htmlOutput;
}
/**
* Displays form tabs which are given by an array indexed by fieldset id
* ({@link PMA_displayFieldsetTop}), with values being tab titles.
*
* @param array $tabs tab names
*
* @return string
*/
function PMA_displayTabsTop($tabs)
{
$items = array();
foreach ($tabs as $tab_id => $tab_name) {
$items[] = array(
'content' => htmlspecialchars($tab_name),
'url' => array(
'href' => '#' . $tab_id,
),
);
}
$htmlOutput = Template::get('list/unordered')->render(
array(
'class' => 'tabs',
'items' => $items,
)
);
$htmlOutput .= '<br clear="right" />';
$htmlOutput .= '<div class="tabs_contents">';
return $htmlOutput;
}
/**
* Displays top part of a fieldset
*
* @param string $title title of fieldset
* @param string $description description shown on top of fieldset
* @param array $errors error messages to display
* @param array $attributes optional extra attributes of fieldset
*
* @return string
*/
function PMA_displayFieldsetTop($title = '', $description = '', $errors = null,
$attributes = array()
) {
global $_FormDisplayGroup;
$_FormDisplayGroup = 0;
$attributes = array_merge(array('class' => 'optbox'), $attributes);
foreach ($attributes as $k => &$attr) {
$attr = $k . '="' . htmlspecialchars($attr) . '"';
}
$htmlOutput = '<fieldset ' . implode(' ', $attributes) . '>';
$htmlOutput .= '<legend>' . $title . '</legend>';
if (!empty($description)) {
$htmlOutput .= '<p>' . $description . '</p>';
}
// this must match with displayErrors() in scripts.js
if (is_array($errors) && count($errors) > 0) {
$htmlOutput .= '<dl class="errors">';
foreach ($errors as $error) {
$htmlOutput .= '<dd>' . $error . '</dd>';
}
$htmlOutput .= '</dl>';
}
$htmlOutput .= '<table width="100%" cellspacing="0">';
return $htmlOutput;
}
/**
* Displays input field
*
* $opts keys:
* o doc - (string) documentation link
* o errors - error array
* o setvalue - (string) shows button allowing to set predefined value
* o show_restore_default - (boolean) whether show "restore default" button
* o userprefs_allow - whether user preferences are enabled for this field
* (null - no support, true/false - enabled/disabled)
* o userprefs_comment - (string) field comment
* o values - key - value pairs for <select> fields
* o values_escaped - (boolean) tells whether values array is already escaped
* (defaults to false)
* o values_disabled - (array)list of disabled values (keys from values)
* o comment - (string) tooltip comment
* o comment_warning - (bool) whether this comments warns about something
*
* @param string $path config option path
* @param string $name config option name
* @param string $type type of config option
* @param mixed $value current value
* @param string $description verbose description
* @param bool $value_is_default whether value is default
* @param array $opts see above description
*
* @return string
*/
function PMA_displayInput($path, $name, $type, $value, $description = '',
$value_is_default = true, $opts = null
) {
global $_FormDisplayGroup;
static $icons; // An array of IMG tags used further below in the function
if (defined('TESTSUITE')) {
$icons = null;
}
$is_setup_script = defined('PMA_SETUP');
if ($icons === null) { // if the static variables have not been initialised
$icons = array();
// Icon definitions:
// The same indexes will be used in the $icons array.
// The first element contains the filename and the second
// element is used for the "alt" and "title" attributes.
$icon_init = array(
'edit' => array('b_edit.png', ''),
'help' => array('b_help.png', __('Documentation')),
'reload' => array('s_reload.png', ''),
'tblops' => array('b_tblops.png', '')
);
if ($is_setup_script) {
// When called from the setup script, we don't have access to the
// sprite-aware getImage() function because the PMA_theme class
// has not been loaded, so we generate the img tags manually.
foreach ($icon_init as $k => $v) {
$title = '';
if (! empty($v[1])) {
$title = ' title="' . $v[1] . '"';
}
$icons[$k] = sprintf(
'<img alt="%s" src="%s"%s />',
$v[1],
".{$GLOBALS['cfg']['ThemePath']}/original/img/{$v[0]}",
$title
);
}
} else {
// In this case we just use getImage() because it's available
foreach ($icon_init as $k => $v) {
$icons[$k] = Util::getImage(
$v[0], $v[1]
);
}
}
}
$has_errors = isset($opts['errors']) && !empty($opts['errors']);
$option_is_disabled = ! $is_setup_script && isset($opts['userprefs_allow'])
&& ! $opts['userprefs_allow'];
$name_id = 'name="' . htmlspecialchars($path) . '" id="'
. htmlspecialchars($path) . '"';
$field_class = $type == 'checkbox' ? 'checkbox' : '';
if (! $value_is_default) {
$field_class .= ($field_class == '' ? '' : ' ')
. ($has_errors ? 'custom field-error' : 'custom');
}
$field_class = $field_class ? ' class="' . $field_class . '"' : '';
$tr_class = $_FormDisplayGroup > 0
? 'group-field group-field-' . $_FormDisplayGroup
: '';
if (isset($opts['setvalue']) && $opts['setvalue'] == ':group') {
unset($opts['setvalue']);
$_FormDisplayGroup++;
$tr_class = 'group-header-field group-header-' . $_FormDisplayGroup;
}
if ($option_is_disabled) {
$tr_class .= ($tr_class ? ' ' : '') . 'disabled-field';
}
$tr_class = $tr_class ? ' class="' . $tr_class . '"' : '';
$htmlOutput = '<tr' . $tr_class . '>';
$htmlOutput .= '<th>';
$htmlOutput .= '<label for="' . htmlspecialchars($path) . '">' . $name
. '</label>';
if (! empty($opts['doc'])) {
$htmlOutput .= '<span class="doc">';
$htmlOutput .= '<a href="' . $opts['doc']
. '" target="documentation">' . $icons['help'] . '</a>';
$htmlOutput .= "\n";
$htmlOutput .= '</span>';
}
if ($option_is_disabled) {
$htmlOutput .= '<span class="disabled-notice" title="';
$htmlOutput .= __(
'This setting is disabled, it will not be applied to your configuration.'
);
$htmlOutput .= '">' . __('Disabled') . "</span>";
}
if (!empty($description)) {
$htmlOutput .= '<small>' . $description . '</small>';
}
$htmlOutput .= '</th>';
$htmlOutput .= '<td>';
switch ($type) {
case 'text':
$htmlOutput .= '<input type="text" size="40" ' . $name_id . $field_class
. ' value="' . htmlspecialchars($value) . '" />';
break;
case 'password':
$htmlOutput .= '<input type="password" size="40" ' . $name_id . $field_class
. ' value="' . htmlspecialchars($value) . '" />';
break;
case 'short_text':
// As seen in the reporting server (#15042) we sometimes receive
// an array here. No clue about its origin nor content, so let's avoid
// a notice on htmlspecialchars().
if (! is_array($value)) {
$htmlOutput .= '<input type="text" size="25" ' . $name_id
. $field_class . ' value="' . htmlspecialchars($value)
. '" />';
}
break;
case 'number_text':
$htmlOutput .= '<input type="number" ' . $name_id . $field_class
. ' value="' . htmlspecialchars($value) . '" />';
break;
case 'checkbox':
$htmlOutput .= '<span' . $field_class . '><input type="checkbox" ' . $name_id
. ($value ? ' checked="checked"' : '') . ' /></span>';
break;
case 'select':
$htmlOutput .= '<select ' . $name_id . $field_class . '>';
$escape = !(isset($opts['values_escaped']) && $opts['values_escaped']);
$values_disabled = isset($opts['values_disabled'])
? array_flip($opts['values_disabled']) : array();
foreach ($opts['values'] as $opt_value_key => $opt_value) {
// set names for boolean values
if (is_bool($opt_value)) {
$opt_value = mb_strtolower(
$opt_value ? __('Yes') : __('No')
);
}
// escape if necessary
if ($escape) {
$display = htmlspecialchars($opt_value);
$display_value = htmlspecialchars($opt_value_key);
} else {
$display = $opt_value;
$display_value = $opt_value_key;
}
// compare with selected value
// boolean values are cast to integers when used as array keys
$selected = is_bool($value)
? (int) $value === $opt_value_key
: $opt_value_key === $value;
$htmlOutput .= '<option value="' . $display_value . '"';
if ($selected) {
$htmlOutput .= ' selected="selected"';
}
if (isset($values_disabled[$opt_value_key])) {
$htmlOutput .= ' disabled="disabled"';
}
$htmlOutput .= '>' . $display . '</option>';
}
$htmlOutput .= '</select>';
break;
case 'list':
$htmlOutput .= '<textarea cols="40" rows="5" ' . $name_id . $field_class
. '>' . htmlspecialchars(implode("\n", $value)) . '</textarea>';
break;
}
if (isset($opts['comment']) && $opts['comment']) {
$class = 'field-comment-mark';
if (isset($opts['comment_warning']) && $opts['comment_warning']) {
$class .= ' field-comment-warning';
}
$htmlOutput .= '<span class="' . $class . '" title="'
. htmlspecialchars($opts['comment']) . '">i</span>';
}
if ($is_setup_script
&& isset($opts['userprefs_comment'])
&& $opts['userprefs_comment']
) {
$htmlOutput .= '<a class="userprefs-comment" title="'
. htmlspecialchars($opts['userprefs_comment']) . '">'
. $icons['tblops'] . '</a>';
}
if (isset($opts['setvalue']) && $opts['setvalue']) {
$htmlOutput .= '<a class="set-value" href="#'
. htmlspecialchars("$path={$opts['setvalue']}") . '" title="'
. sprintf(__('Set value: %s'), htmlspecialchars($opts['setvalue']))
. '" style="display:none">' . $icons['edit'] . '</a>';
}
if (isset($opts['show_restore_default']) && $opts['show_restore_default']) {
$htmlOutput .= '<a class="restore-default" href="#' . $path . '" title="'
. __('Restore default value') . '" style="display:none">'
. $icons['reload'] . '</a>';
}
// this must match with displayErrors() in scripts/config.js
if ($has_errors) {
$htmlOutput .= "\n <dl class=\"inline_errors\">";
foreach ($opts['errors'] as $error) {
$htmlOutput .= '<dd>' . htmlspecialchars($error) . '</dd>';
}
$htmlOutput .= '</dl>';
}
$htmlOutput .= '</td>';
if ($is_setup_script && isset($opts['userprefs_allow'])) {
$htmlOutput .= '<td class="userprefs-allow" title="' .
__('Allow users to customize this value') . '">';
$htmlOutput .= '<input type="checkbox" name="' . $path
. '-userprefs-allow" ';
if ($opts['userprefs_allow']) {
$htmlOutput .= 'checked="checked"';
};
$htmlOutput .= '/>';
$htmlOutput .= '</td>';
} else if ($is_setup_script) {
$htmlOutput .= '<td>&nbsp;</td>';
}
$htmlOutput .= '</tr>';
return $htmlOutput;
}
/**
* Display group header
*
* @param string $header_text Text of header
*
* @return string|void
*/
function PMA_displayGroupHeader($header_text)
{
global $_FormDisplayGroup;
$_FormDisplayGroup++;
if (! $header_text) {
return null;
}
$colspan = defined('PMA_SETUP')
? 3
: 2;
$htmlOutput = '<tr class="group-header group-header-' . $_FormDisplayGroup
. '">';
$htmlOutput .= '<th colspan="' . $colspan . '">';
$htmlOutput .= $header_text;
$htmlOutput .= '</th>';
$htmlOutput .= '</tr>';
return $htmlOutput;
}
/**
* Display group footer
*
* @return void
*/
function PMA_displayGroupFooter()
{
global $_FormDisplayGroup;
$_FormDisplayGroup--;
}
/**
* Displays bottom part of a fieldset
*
* @param bool $show_buttons whether show submit and reset button
*
* @return string
*/
function PMA_displayFieldsetBottom($show_buttons = true)
{
$colspan = 2;
if (defined('PMA_SETUP')) {
$colspan++;
}
$htmlOutput = '';
if ($show_buttons) {
$htmlOutput .= '<tr>';
$htmlOutput .= '<td colspan="' . $colspan . '" class="lastrow">';
$htmlOutput .= '<input type="submit" name="submit_save" value="'
. __('Apply') . '" class="green" />';
$htmlOutput .= '<input type="button" name="submit_reset" value="'
. __('Reset') . '" />';
$htmlOutput .= '</td>';
$htmlOutput .= '</tr>';
}
$htmlOutput .= '</table>';
$htmlOutput .= '</fieldset>';
return $htmlOutput;
}
/**
* Displays simple bottom part of a fieldset (without submit buttons)
*
* @return string
*/
function PMA_displayFieldsetBottomSimple()
{
$htmlOutput = '</table>';
$htmlOutput .= '</fieldset>';
return $htmlOutput;
}
/**
* Closes form tabs
*
* @return string
*/
function PMA_displayTabsBottom()
{
$htmlOutput = "</div>\n";
return $htmlOutput;
}
/**
* Displays bottom part of the form
*
* @return string
*/
function PMA_displayFormBottom()
{
$htmlOutput = "</form>\n";
return $htmlOutput;
}
/**
* Appends JS validation code to $js_array
*
* @param string $field_id ID of field to validate
* @param string|array $validators validators callback
* @param array &$js_array will be updated with javascript code
*
* @return void
*/
function PMA_addJsValidate($field_id, $validators, &$js_array)
{
foreach ((array)$validators as $validator) {
$validator = (array)$validator;
$v_name = array_shift($validator);
$v_name = "PMA_" . $v_name;
$v_args = array();
foreach ($validator as $arg) {
$v_args[] = PMA_escapeJsString($arg);
}
$v_args = $v_args ? ", ['" . implode("', '", $v_args) . "']" : '';
$js_array[] = "validateField('$field_id', '$v_name', true$v_args)";
}
}
/**
* Displays JavaScript code
*
* @param array $js_array lines of javascript code
*
* @return string
*/
function PMA_displayJavascript($js_array)
{
if (empty($js_array)) {
return null;
}
return Template::get('javascript/display')->render(
array('js_array' => $js_array,)
);
}
/**
* Displays error list
*
* @param string $name name of item with errors
* @param array $error_list list of errors to show
*
* @return string HTML for errors
*/
function PMA_displayErrors($name, $error_list)
{
$htmlOutput = '<dl>';
$htmlOutput .= '<dt>' . htmlspecialchars($name) . '</dt>';
foreach ($error_list as $error) {
$htmlOutput .= '<dd>' . htmlspecialchars($error) . '</dd>';
}
$htmlOutput .= '</dl>';
return $htmlOutput;
}

View File

@ -0,0 +1,231 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Page-related settings
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\config;
use PMA\libraries\Message;
use PMA\libraries\Response;
require_once 'libraries/user_preferences.lib.php';
require_once 'libraries/config/config_functions.lib.php';
require_once 'libraries/config/messages.inc.php';
require 'libraries/config/user_preferences.forms.php';
require 'libraries/config/page_settings.forms.php';
/**
* Page-related settings
*
* @package PhpMyAdmin
*/
class PageSettings
{
/**
* Contains id of the form element
* @var string
*/
private $_elemId = 'page_settings_modal';
/**
* Name of the group to show
* @var string
*/
private $_groupName = '';
/**
* Contains HTML of errors
* @var string
*/
private $_errorHTML = '';
/**
* Contains HTML of settings
* @var string
*/
private $_HTML = '';
/**
* Constructor
*
* @param string $formGroupName The name of config form group to display
* @param string $elemId Id of the div containing settings
*/
public function __construct($formGroupName, $elemId = null)
{
global $forms;
if (empty($forms[$formGroupName])) {
return;
}
if (isset($_REQUEST['printview']) && $_REQUEST['printview'] == '1') {
return;
}
if (!empty($elemId)) {
$this->_elemId = $elemId;
}
$this->_groupName = $formGroupName;
$cf = new ConfigFile($GLOBALS['PMA_Config']->base_settings);
PMA_userprefsPageInit($cf);
$form_display = new FormDisplay($cf);
foreach ($forms[$formGroupName] as $form_name => $form) {
// skip Developer form if no setting is available
if ($form_name == 'Developer'
&& !$GLOBALS['cfg']['UserprefsDeveloperTab']
) {
continue;
}
$form_display->registerForm($form_name, $form, 1);
}
// Process form
$error = null;
if (isset($_POST['submit_save'])
&& $_POST['submit_save'] == $formGroupName
) {
$this->_processPageSettings($form_display, $cf, $error);
}
// Display forms
$this->_HTML = $this->_getPageSettingsDisplay($form_display, $error);
}
/**
* Process response to form
*
* @param FormDisplay &$form_display Form
* @param ConfigFile &$cf Configuration file
* @param Message|null &$error Error message
*
* @return void
*/
private function _processPageSettings(&$form_display, &$cf, &$error)
{
if ($form_display->process(false) && !$form_display->hasErrors()) {
// save settings
$result = PMA_saveUserprefs($cf->getConfigArray());
if ($result === true) {
// reload page
header('Location: ' . $_SERVER['REQUEST_URI']);
exit();
} else {
$error = $result;
}
}
}
/**
* Store errors in _errorHTML
*
* @param FormDisplay &$form_display Form
* @param Message|null &$error Error message
*
* @return void
*/
private function _storeError(&$form_display, &$error)
{
$retval = '';
if ($error) {
$retval .= $error->getDisplay();
}
if ($form_display->hasErrors()) {
// form has errors
$retval .= '<div class="error config-form">'
. '<b>' . __(
'Cannot save settings, submitted configuration form contains '
. 'errors!'
) . '</b>'
. $form_display->displayErrors()
. '</div>';
}
$this->_errorHTML = $retval;
}
/**
* Display page-related settings
*
* @param FormDisplay &$form_display Form
* @param Message &$error Error message
*
* @return string
*/
private function _getPageSettingsDisplay(&$form_display, &$error)
{
$response = Response::getInstance();
$retval = '';
$this->_storeError($form_display, $error);
$retval .= '<div id="' . $this->_elemId . '">';
$retval .= '<div class="page_settings">';
$retval .= $form_display->getDisplay(
true,
true,
false,
$response->getFooter()->getSelfUrl('unencoded'),
array(
'submit_save' => $this->_groupName
)
);
$retval .= '</div>';
$retval .= '</div>';
return $retval;
}
/**
* Get HTML output
*
* @return string
*/
public function getHTML()
{
return $this->_HTML;
}
/**
* Get error HTML output
*
* @return string
*/
public function getErrorHTML()
{
return $this->_errorHTML;
}
/**
* Group to show for Page-related settings
* @param string $formGroupName The name of config form group to display
* @return PageSettings
*/
public static function showGroup($formGroupName)
{
$object = new PageSettings($formGroupName);
$response = Response::getInstance();
$response->addHTML($object->getErrorHTML());
$response->addHTML($object->getHTML());
return $object;
}
/**
* Get HTML for navigation settings
* @return string
*/
public static function getNaviSettings()
{
$object = new PageSettings('Navi_panel', 'pma_navigation_settings');
$response = Response::getInstance();
$response->addHTML($object->getErrorHTML());
return $object->getHTML();
}
}

View File

@ -0,0 +1,646 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Server config checks management
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\config;
/**
* Performs various compatibility, security and consistency checks on current config
*
* Outputs results to message list, must be called between PMA_messagesBegin()
* and PMA_messagesEnd()
*
* @package PhpMyAdmin
*/
class ServerConfigChecks
{
/**
* @var ConfigFile configurations being checked
*/
protected $cfg;
/**
* Constructor.
*
* @param ConfigFile $cfg Configuration
*/
public function __construct(ConfigFile $cfg)
{
$this->cfg = $cfg;
}
/**
* Perform config checks
*
* @return void
*/
public function performConfigChecks()
{
$blowfishSecret = $this->cfg->get('blowfish_secret');
$blowfishSecretSet = false;
$cookieAuthUsed = false;
list(
$sAllowArbitraryServerWarn, $sBlowfishSecretMsg,
$sBZipDumpWarn, $sDirectoryNotice,
$sGZipDumpWarn, $sLoginCookieValidityWarn,
$sLoginCookieValidityWarn2, $sLoginCookieValidityWarn3,
$sSecurityInfoMsg, $sSrvAuthCfgMsg, $sZipDumpExportWarn,
$sZipDumpImportWarn
) = self::defineMessages();
list($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet)
= $this->performConfigChecksServers(
$cookieAuthUsed, $blowfishSecret, $sSrvAuthCfgMsg,
$sSecurityInfoMsg, $blowfishSecretSet
);
$this->performConfigChecksCookieAuthUsed(
$cookieAuthUsed, $blowfishSecretSet, $sBlowfishSecretMsg,
$blowfishSecret
);
//
// $cfg['AllowArbitraryServer']
// should be disabled
//
if ($this->cfg->getValue('AllowArbitraryServer')) {
PMA_messagesSet(
'notice',
'AllowArbitraryServer',
PMA_lang(PMA_langName('AllowArbitraryServer')),
PMA_lang($sAllowArbitraryServerWarn)
);
}
$this->performConfigChecksLoginCookie(
$sLoginCookieValidityWarn, $sLoginCookieValidityWarn2,
$sLoginCookieValidityWarn3
);
//
// $cfg['SaveDir']
// should not be world-accessible
//
if ($this->cfg->getValue('SaveDir') != '') {
PMA_messagesSet(
'notice',
'SaveDir',
PMA_lang(PMA_langName('SaveDir')),
PMA_lang($sDirectoryNotice)
);
}
//
// $cfg['TempDir']
// should not be world-accessible
//
if ($this->cfg->getValue('TempDir') != '') {
PMA_messagesSet(
'notice',
'TempDir',
PMA_lang(PMA_langName('TempDir')),
PMA_lang($sDirectoryNotice)
);
}
$this->performConfigChecksZips(
$sGZipDumpWarn, $sBZipDumpWarn, $sZipDumpImportWarn,
$sZipDumpExportWarn
);
}
/**
* Check config of servers
*
* @param boolean $cookieAuthUsed Cookie auth is used
* @param string $blowfishSecret Blowfish secret
* @param string $sServerAuthCfgMsg Message for server auth config
* @param string $sSecurityInfoMsg Message for security information
* @param boolean $blowfishSecretSet Blowfish secret set
*
* @return array
*/
protected function performConfigChecksServers(
$cookieAuthUsed, $blowfishSecret, $sServerAuthCfgMsg,
$sSecurityInfoMsg, $blowfishSecretSet
) {
$serverCnt = $this->cfg->getServerCount();
for ($i = 1; $i <= $serverCnt; $i++) {
$cookieAuthServer
= ($this->cfg->getValue("Servers/$i/auth_type") == 'cookie');
$cookieAuthUsed |= $cookieAuthServer;
$serverName = $this->performConfigChecksServersGetServerName(
$this->cfg->getServerName($i), $i
);
$serverName = htmlspecialchars($serverName);
list($blowfishSecret, $blowfishSecretSet)
= $this->performConfigChecksServersSetBlowfishSecret(
$blowfishSecret, $cookieAuthServer, $blowfishSecretSet
);
//
// $cfg['Servers'][$i]['ssl']
// should be enabled if possible
//
if (!$this->cfg->getValue("Servers/$i/ssl")) {
$title = PMA_lang(PMA_langName('Servers/1/ssl')) . " ($serverName)";
PMA_messagesSet(
'notice',
"Servers/$i/ssl",
$title,
__(
'You should use SSL connections if your database server '
. 'supports it.'
)
);
}
//
// $cfg['Servers'][$i]['auth_type']
// warn about full user credentials if 'auth_type' is 'config'
//
if ($this->cfg->getValue("Servers/$i/auth_type") == 'config'
&& $this->cfg->getValue("Servers/$i/user") != ''
&& $this->cfg->getValue("Servers/$i/password") != ''
) {
$title = PMA_lang(PMA_langName('Servers/1/auth_type'))
. " ($serverName)";
PMA_messagesSet(
'notice',
"Servers/$i/auth_type",
$title,
PMA_lang($sServerAuthCfgMsg, $i) . ' '
. PMA_lang($sSecurityInfoMsg, $i)
);
}
//
// $cfg['Servers'][$i]['AllowRoot']
// $cfg['Servers'][$i]['AllowNoPassword']
// serious security flaw
//
if ($this->cfg->getValue("Servers/$i/AllowRoot")
&& $this->cfg->getValue("Servers/$i/AllowNoPassword")
) {
$title = PMA_lang(PMA_langName('Servers/1/AllowNoPassword'))
. " ($serverName)";
PMA_messagesSet(
'notice',
"Servers/$i/AllowNoPassword",
$title,
__('You allow for connecting to the server without a password.')
. ' ' . PMA_lang($sSecurityInfoMsg, $i)
);
}
}
return array($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet);
}
/**
* Set blowfish secret
*
* @param string $blowfishSecret Blowfish secret
* @param boolean $cookieAuthServer Cookie auth is used
* @param boolean $blowfishSecretSet Blowfish secret set
*
* @return array
*/
protected function performConfigChecksServersSetBlowfishSecret(
$blowfishSecret, $cookieAuthServer, $blowfishSecretSet
) {
if ($cookieAuthServer && $blowfishSecret === null) {
$blowfishSecret = '';
if (! function_exists('openssl_random_pseudo_bytes')) {
$random_func = 'phpseclib\\Crypt\\Random::string';
} else {
$random_func = 'openssl_random_pseudo_bytes';
}
while (strlen($blowfishSecret) < 32) {
$byte = $random_func(1);
// We want only ASCII chars
if (ord($byte) > 32 && ord($byte) < 127) {
$blowfishSecret .= $byte;
}
}
$blowfishSecretSet = true;
$this->cfg->set('blowfish_secret', $blowfishSecret);
}
return array($blowfishSecret, $blowfishSecretSet);
}
/**
* Define server name
*
* @param string $serverName Server name
* @param int $serverId Server id
*
* @return string Server name
*/
protected function performConfigChecksServersGetServerName(
$serverName, $serverId
) {
if ($serverName == 'localhost') {
$serverName .= " [$serverId]";
return $serverName;
}
return $serverName;
}
/**
* Perform config checks for zip part.
*
* @param string $sGZipDumpWarning Gzip dump warning
* @param string $sBZipDumpWarning Bzip dump warning
* @param string $sZipDumpImportWarn Zip dump import warning
* @param string $sZipDumpExportWarn Zip dump export warning
*
* @return void
*/
protected function performConfigChecksZips(
$sGZipDumpWarning, $sBZipDumpWarning, $sZipDumpImportWarn,
$sZipDumpExportWarn
) {
$this->performConfigChecksServerGZipdump($sGZipDumpWarning);
$this->performConfigChecksServerBZipdump($sBZipDumpWarning);
$this->performConfigChecksServersZipdump(
$sZipDumpImportWarn, $sZipDumpExportWarn
);
}
/**
* Perform config checks for zip part.
*
* @param string $sZipDumpImportWarn Zip dump import warning
* @param string $sZipDumpExportWarn Zip dump export warning
*
* @return void
*/
protected function performConfigChecksServersZipdump(
$sZipDumpImportWarn, $sZipDumpExportWarn
) {
//
// $cfg['ZipDump']
// requires zip_open in import
//
if ($this->cfg->getValue('ZipDump') && !@function_exists('zip_open')) {
PMA_messagesSet(
'error',
'ZipDump_import',
PMA_lang(PMA_langName('ZipDump')),
PMA_lang($sZipDumpImportWarn, 'zip_open')
);
}
//
// $cfg['ZipDump']
// requires gzcompress in export
//
if ($this->cfg->getValue('ZipDump') && !@function_exists('gzcompress')) {
PMA_messagesSet(
'error',
'ZipDump_export',
PMA_lang(PMA_langName('ZipDump')),
PMA_lang($sZipDumpExportWarn, 'gzcompress')
);
}
}
/**
* Check config of servers
*
* @param boolean $cookieAuthUsed Cookie auth is used
* @param boolean $blowfishSecretSet Blowfish secret set
* @param string $sBlowfishSecretMsg Blowfish secret message
* @param string $blowfishSecret Blowfish secret
*
* @return array
*/
protected function performConfigChecksCookieAuthUsed(
$cookieAuthUsed, $blowfishSecretSet, $sBlowfishSecretMsg,
$blowfishSecret
) {
//
// $cfg['blowfish_secret']
// it's required for 'cookie' authentication
//
if ($cookieAuthUsed) {
if ($blowfishSecretSet) {
// 'cookie' auth used, blowfish_secret was generated
PMA_messagesSet(
'notice',
'blowfish_secret_created',
PMA_lang(PMA_langName('blowfish_secret')),
PMA_lang($sBlowfishSecretMsg)
);
} else {
$blowfishWarnings = array();
// check length
if (strlen($blowfishSecret) < 32) {
// too short key
$blowfishWarnings[] = __(
'Key is too short, it should have at least 32 characters.'
);
}
// check used characters
$hasDigits = (bool)preg_match('/\d/', $blowfishSecret);
$hasChars = (bool)preg_match('/\S/', $blowfishSecret);
$hasNonword = (bool)preg_match('/\W/', $blowfishSecret);
if (!$hasDigits || !$hasChars || !$hasNonword) {
$blowfishWarnings[] = PMA_lang(
__(
'Key should contain letters, numbers [em]and[/em] '
. 'special characters.'
)
);
}
if (!empty($blowfishWarnings)) {
PMA_messagesSet(
'error',
'blowfish_warnings' . count($blowfishWarnings),
PMA_lang(PMA_langName('blowfish_secret')),
implode('<br />', $blowfishWarnings)
);
}
}
}
}
/**
* Define all messages
*
* @return array
*/
protected static function defineMessages()
{
$sAllowArbitraryServerWarn = __(
'This %soption%s should be disabled as it allows attackers to '
. 'bruteforce login to any MySQL server. If you feel this is necessary, '
. 'use %srestrict login to MySQL server%s or %strusted proxies list%s. '
. 'However, IP-based protection with trusted proxies list may not be '
. 'reliable if your IP belongs to an ISP where thousands of users, '
. 'including you, are connected to.'
);
$sAllowArbitraryServerWarn = sprintf(
$sAllowArbitraryServerWarn,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]',
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]',
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]'
);
$sBlowfishSecretMsg = __(
'You didn\'t have blowfish secret set and have enabled '
. '[kbd]cookie[/kbd] authentication, so a key was automatically '
. 'generated for you. It is used to encrypt cookies; you don\'t need to '
. 'remember it.'
);
$sBZipDumpWarning = __(
'%sBzip2 compression and decompression%s requires functions (%s) which '
. 'are unavailable on this system.'
);
$sBZipDumpWarning = sprintf(
$sBZipDumpWarning,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Import_export]',
'[/a]', '%s'
);
$sDirectoryNotice = __(
'This value should be double checked to ensure that this directory is '
. 'neither world accessible nor readable or writable by other users on '
. 'your server.'
);
$sGZipDumpWarning = __(
'%sGZip compression and decompression%s requires functions (%s) which '
. 'are unavailable on this system.'
);
$sGZipDumpWarning = sprintf(
$sGZipDumpWarning,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Import_export]',
'[/a]',
'%s'
);
$sLoginCookieValidityWarn = __(
'%sLogin cookie validity%s greater than %ssession.gc_maxlifetime%s may '
. 'cause random session invalidation (currently session.gc_maxlifetime '
. 'is %d).'
);
$sLoginCookieValidityWarn = sprintf(
$sLoginCookieValidityWarn,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]',
'[a@' . PMA_getPHPDocLink(
'session.configuration.php#ini.session.gc-maxlifetime'
) . ']',
'[/a]',
ini_get('session.gc_maxlifetime')
);
$sLoginCookieValidityWarn2 = __(
'%sLogin cookie validity%s should be set to 1800 seconds (30 minutes) '
. 'at most. Values larger than 1800 may pose a security risk such as '
. 'impersonation.'
);
$sLoginCookieValidityWarn2 = sprintf(
$sLoginCookieValidityWarn2,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]'
);
$sLoginCookieValidityWarn3 = __(
'If using [kbd]cookie[/kbd] authentication and %sLogin cookie store%s '
. 'is not 0, %sLogin cookie validity%s must be set to a value less or '
. 'equal to it.'
);
$sLoginCookieValidityWarn3 = sprintf(
$sLoginCookieValidityWarn3,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]',
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]'
);
$sSecurityInfoMsg = __(
'If you feel this is necessary, use additional protection settings - '
. '%shost authentication%s settings and %strusted proxies list%s. '
. 'However, IP-based protection may not be reliable if your IP belongs '
. 'to an ISP where thousands of users, including you, are connected to.'
);
$sSecurityInfoMsg = sprintf(
$sSecurityInfoMsg,
'[a@?page=servers' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;mode=edit&amp;id=%1$d#tab_Server_config]',
'[/a]',
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Security]',
'[/a]'
);
$sServerAuthConfigMsg = __(
'You set the [kbd]config[/kbd] authentication type and included '
. 'username and password for auto-login, which is not a desirable '
. 'option for live hosts. Anyone who knows or guesses your phpMyAdmin '
. 'URL can directly access your phpMyAdmin panel. Set %sauthentication '
. 'type%s to [kbd]cookie[/kbd] or [kbd]http[/kbd].'
);
$sServerAuthConfigMsg = sprintf(
$sServerAuthConfigMsg,
'[a@?page=servers' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;mode=edit&amp;id=%1$d#tab_Server]',
'[/a]'
);
$sZipDumpExportWarn = __(
'%sZip compression%s requires functions (%s) which are unavailable on '
. 'this system.'
);
$sZipDumpExportWarn = sprintf(
$sZipDumpExportWarn,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Import_export]',
'[/a]',
'%s'
);
$sZipDumpImportWarn = __(
'%sZip decompression%s requires functions (%s) which are unavailable '
. 'on this system.'
);
$sZipDumpImportWarn = sprintf(
$sZipDumpImportWarn,
'[a@?page=form' . PMA_URL_getCommon(array(), 'html', '&')
. '&amp;formset=Features#tab_Import_export]',
'[/a]',
'%s'
);
return array(
$sAllowArbitraryServerWarn, $sBlowfishSecretMsg, $sBZipDumpWarning,
$sDirectoryNotice, $sGZipDumpWarning,
$sLoginCookieValidityWarn, $sLoginCookieValidityWarn2,
$sLoginCookieValidityWarn3, $sSecurityInfoMsg, $sServerAuthConfigMsg,
$sZipDumpExportWarn, $sZipDumpImportWarn
);
}
/**
* Check configuration for login cookie
*
* @param string $sLoginCookieValidityWarn Warning 1 for login cookie validity
* @param string $sLoginCookieValidityWarn2 Warning 2 for login cookie validity
* @param string $sLoginCookieValidityWarn3 Warning 3 for login cookie validity
*
* @return void
*/
protected function performConfigChecksLoginCookie(
$sLoginCookieValidityWarn, $sLoginCookieValidityWarn2,
$sLoginCookieValidityWarn3
) {
//
// $cfg['LoginCookieValidity']
// value greater than session.gc_maxlifetime will cause
// random session invalidation after that time
$loginCookieValidity = $this->cfg->getValue('LoginCookieValidity');
if ($loginCookieValidity > ini_get('session.gc_maxlifetime')
) {
PMA_messagesSet(
'error',
'LoginCookieValidity',
PMA_lang(PMA_langName('LoginCookieValidity')),
PMA_lang($sLoginCookieValidityWarn)
);
}
//
// $cfg['LoginCookieValidity']
// should be at most 1800 (30 min)
//
if ($loginCookieValidity > 1800) {
PMA_messagesSet(
'notice',
'LoginCookieValidity',
PMA_lang(PMA_langName('LoginCookieValidity')),
PMA_lang($sLoginCookieValidityWarn2)
);
}
//
// $cfg['LoginCookieValidity']
// $cfg['LoginCookieStore']
// LoginCookieValidity must be less or equal to LoginCookieStore
//
if (($this->cfg->getValue('LoginCookieStore') != 0)
&& ($loginCookieValidity > $this->cfg->getValue('LoginCookieStore'))
) {
PMA_messagesSet(
'error',
'LoginCookieValidity',
PMA_lang(PMA_langName('LoginCookieValidity')),
PMA_lang($sLoginCookieValidityWarn3)
);
}
}
/**
* Check GZipDump configuration
*
* @param string $sBZipDumpWarn Warning for BZipDumpWarning
*
* @return void
*/
protected function performConfigChecksServerBZipdump($sBZipDumpWarn)
{
//
// $cfg['BZipDump']
// requires bzip2 functions
//
if ($this->cfg->getValue('BZipDump')
&& (!@function_exists('bzopen') || !@function_exists('bzcompress'))
) {
$functions = @function_exists('bzopen')
? '' :
'bzopen';
$functions .= @function_exists('bzcompress')
? ''
: ($functions ? ', ' : '') . 'bzcompress';
PMA_messagesSet(
'error',
'BZipDump',
PMA_lang(PMA_langName('BZipDump')),
PMA_lang($sBZipDumpWarn, $functions)
);
}
}
/**
* Check GZipDump configuration
*
* @param string $sGZipDumpWarn Warning for GZipDumpWarning
*
* @return void
*/
protected function performConfigChecksServerGZipdump($sGZipDumpWarn)
{
//
// $cfg['GZipDump']
// requires zlib functions
//
if ($this->cfg->getValue('GZipDump')
&& (@!function_exists('gzopen') || @!function_exists('gzencode'))
) {
PMA_messagesSet(
'error',
'GZipDump',
PMA_lang(PMA_langName('GZipDump')),
PMA_lang($sGZipDumpWarn, 'gzencode')
);
}
}
}

View File

@ -0,0 +1,599 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Form validation for configuration editor
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\config;
use PMA\libraries\DatabaseInterface;
use PMA\libraries\Util;
/**
* Validation class for various validation functions
*
* Validation function takes two argument: id for which it is called
* and array of fields' values (usually values for entire formset, as defined
* in forms.inc.php).
* The function must always return an array with an error (or error array)
* assigned to a form element (formset name or field path). Even if there are
* no errors, key must be set with an empty value.
*
* Validation functions are assigned in $cfg_db['_validators'] (config.values.php).
*
* @package PhpMyAdmin
*/
class Validator
{
/**
* Returns validator list
*
* @param ConfigFile $cf Config file instance
*
* @return array
*/
public static function getValidators(ConfigFile $cf)
{
static $validators = null;
if ($validators !== null) {
return $validators;
}
$validators = $cf->getDbEntry('_validators', array());
if (defined('PMA_SETUP')) {
return $validators;
}
// not in setup script: load additional validators for user
// preferences we need original config values not overwritten
// by user preferences, creating a new PMA\libraries\Config instance is a
// better idea than hacking into its code
$uvs = $cf->getDbEntry('_userValidators', array());
foreach ($uvs as $field => $uv_list) {
$uv_list = (array)$uv_list;
foreach ($uv_list as &$uv) {
if (!is_array($uv)) {
continue;
}
for ($i = 1, $nb = count($uv); $i < $nb; $i++) {
if (mb_substr($uv[$i], 0, 6) == 'value:') {
$uv[$i] = PMA_arrayRead(
mb_substr($uv[$i], 6),
$GLOBALS['PMA_Config']->base_settings
);
}
}
}
$validators[$field] = isset($validators[$field])
? array_merge((array)$validators[$field], $uv_list)
: $uv_list;
}
return $validators;
}
/**
* Runs validation $validator_id on values $values and returns error list.
*
* Return values:
* o array, keys - field path or formset id, values - array of errors
* when $isPostSource is true values is an empty array to allow for error list
* cleanup in HTML document
* o false - when no validators match name(s) given by $validator_id
*
* @param ConfigFile $cf Config file instance
* @param string|array $validator_id ID of validator(s) to run
* @param array &$values Values to validate
* @param bool $isPostSource tells whether $values are directly from
* POST request
*
* @return bool|array
*/
public static function validate(
ConfigFile $cf, $validator_id, &$values, $isPostSource
) {
// find validators
$validator_id = (array) $validator_id;
$validators = static::getValidators($cf);
$vids = array();
foreach ($validator_id as &$vid) {
$vid = $cf->getCanonicalPath($vid);
if (isset($validators[$vid])) {
$vids[] = $vid;
}
}
if (empty($vids)) {
return false;
}
// create argument list with canonical paths and remember path mapping
$arguments = array();
$key_map = array();
foreach ($values as $k => $v) {
$k2 = $isPostSource ? str_replace('-', '/', $k) : $k;
$k2 = mb_strpos($k2, '/')
? $cf->getCanonicalPath($k2)
: $k2;
$key_map[$k2] = $k;
$arguments[$k2] = $v;
}
// validate
$result = array();
foreach ($vids as $vid) {
// call appropriate validation functions
foreach ((array)$validators[$vid] as $validator) {
$vdef = (array) $validator;
$vname = array_shift($vdef);
$vname = 'PMA\libraries\config\Validator::' . $vname;
$args = array_merge(array($vid, &$arguments), $vdef);
$r = call_user_func_array($vname, $args);
// merge results
if (!is_array($r)) {
continue;
}
foreach ($r as $key => $error_list) {
// skip empty values if $isPostSource is false
if (! $isPostSource && empty($error_list)) {
continue;
}
if (! isset($result[$key])) {
$result[$key] = array();
}
$result[$key] = array_merge(
$result[$key], (array)$error_list
);
}
}
}
// restore original paths
$new_result = array();
foreach ($result as $k => $v) {
$k2 = isset($key_map[$k]) ? $key_map[$k] : $k;
$new_result[$k2] = $v;
}
return empty($new_result) ? true : $new_result;
}
/**
* Test database connection
*
* @param string $connect_type 'tcp' or 'socket'
* @param string $host host name
* @param string $port tcp port to use
* @param string $socket socket to use
* @param string $user username to use
* @param string $pass password to use
* @param string $error_key key to use in return array
*
* @return bool|array
*/
public static function testDBConnection(
$connect_type,
$host,
$port,
$socket,
$user,
$pass = null,
$error_key = 'Server'
) {
// static::testPHPErrorMsg();
$error = null;
$host = PMA_sanitizeMySQLHost($host);
if (DatabaseInterface::checkDbExtension('mysqli')) {
$socket = empty($socket) || $connect_type == 'tcp' ? null : $socket;
$port = empty($port) || $connect_type == 'socket' ? null : $port;
$extension = 'mysqli';
} else {
$socket = empty($socket) || $connect_type == 'tcp'
? null
: ':' . ($socket[0] == '/' ? '' : '/') . $socket;
$port = empty($port) || $connect_type == 'socket' ? null : ':' . $port;
$extension = 'mysql';
}
if ($extension == 'mysql') {
$conn = @mysql_connect($host . $port . $socket, $user, $pass);
if (! $conn) {
$error = __('Could not connect to the database server!');
} else {
mysql_close($conn);
}
} else {
$conn = @mysqli_connect($host, $user, $pass, null, $port, $socket);
if (! $conn) {
$error = __('Could not connect to the database server!');
} else {
mysqli_close($conn);
}
}
// static::testPHPErrorMsg(false);
if (isset($php_errormsg)) {
$error .= " - $php_errormsg";
}
return is_null($error) ? true : array($error_key => $error);
}
/**
* Validate server config
*
* @param string $path path to config, not used
* keep this parameter since the method is invoked using
* reflection along with other similar methods
* @param array $values config values
*
* @return array
*/
public static function validateServer($path, $values)
{
$result = array(
'Server' => '',
'Servers/1/user' => '',
'Servers/1/SignonSession' => '',
'Servers/1/SignonURL' => ''
);
$error = false;
if (empty($values['Servers/1/auth_type'])) {
$values['Servers/1/auth_type'] = '';
$result['Servers/1/auth_type'] = __('Invalid authentication type!');
$error = true;
}
if ($values['Servers/1/auth_type'] == 'config'
&& empty($values['Servers/1/user'])
) {
$result['Servers/1/user'] = __(
'Empty username while using [kbd]config[/kbd] authentication method!'
);
$error = true;
}
if ($values['Servers/1/auth_type'] == 'signon'
&& empty($values['Servers/1/SignonSession'])
) {
$result['Servers/1/SignonSession'] = __(
'Empty signon session name '
. 'while using [kbd]signon[/kbd] authentication method!'
);
$error = true;
}
if ($values['Servers/1/auth_type'] == 'signon'
&& empty($values['Servers/1/SignonURL'])
) {
$result['Servers/1/SignonURL'] = __(
'Empty signon URL while using [kbd]signon[/kbd] authentication '
. 'method!'
);
$error = true;
}
if (! $error && $values['Servers/1/auth_type'] == 'config') {
$password = '';
if (! empty($values['Servers/1/password'])) {
$password = $values['Servers/1/password'];
}
$test = static::testDBConnection(
empty($values['Servers/1/connect_type']) ? '' : $values['Servers/1/connect_type'],
empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
empty($values['Servers/1/user']) ? '' : $values['Servers/1/user'],
$password,
'Server'
);
// If failed 'with' password, try 'without' password
if ($test !== true
&& !empty($values['Servers/1/nopassword'])
&& $values['Servers/1/nopassword']
) {
$password = '';
$test = static::testDBConnection(
empty($values['Servers/1/connect_type']) ? '' : $values['Servers/1/connect_type'],
empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
empty($values['Servers/1/user']) ? '' : $values['Servers/1/user'],
$password,
'Server'
);
}
if ($test !== true) {
$result = array_merge($result, $test);
}
}
return $result;
}
/**
* Validate pmadb config
*
* @param string $path path to config, not used
* keep this parameter since the method is invoked using
* reflection along with other similar methods
* @param array $values config values
*
* @return array
*/
public static function validatePMAStorage($path, $values)
{
$result = array(
'Server_pmadb' => '',
'Servers/1/controluser' => '',
'Servers/1/controlpass' => ''
);
$error = false;
if (empty($values['Servers/1/pmadb'])) {
return $result;
}
$result = array();
if (empty($values['Servers/1/controluser'])) {
$result['Servers/1/controluser'] = __(
'Empty phpMyAdmin control user while using phpMyAdmin configuration '
. 'storage!'
);
$error = true;
}
if (empty($values['Servers/1/controlpass'])) {
$result['Servers/1/controlpass'] = __(
'Empty phpMyAdmin control user password while using phpMyAdmin '
. 'configuration storage!'
);
$error = true;
}
if (! $error) {
$test = static::testDBConnection(
empty($values['Servers/1/connect_type']) ? '' : $values['Servers/1/connect_type'],
empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
empty($values['Servers/1/controluser']) ? '' : $values['Servers/1/controluser'],
empty($values['Servers/1/controlpass']) ? '' : $values['Servers/1/controlpass'],
'Server_pmadb'
);
if ($test !== true) {
$result = array_merge($result, $test);
}
}
return $result;
}
/**
* Validates regular expression
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validateRegex($path, $values)
{
$result = array($path => '');
if (empty($values[$path])) {
return $result;
}
if (function_exists('error_clear_last')) {
/* PHP 7 only code */
error_clear_last();
$last_error = null;
} else {
// As fallback we trigger another error to ensure
// that preg error will be different
@strpos();
$last_error = error_get_last();
}
$matches = array();
// in libraries/ListDatabase.php _checkHideDatabase(),
// a '/' is used as the delimiter for hide_db
@preg_match('/' . Util::requestString($values[$path]) . '/', '', $matches);
$current_error = error_get_last();
if ($current_error !== $last_error) {
$error = preg_replace('/^preg_match\(\): /', '', $current_error['message']);
return array($path => $error);
}
return $result;
}
/**
* Validates TrustedProxies field
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validateTrustedProxies($path, $values)
{
$result = array($path => array());
if (empty($values[$path])) {
return $result;
}
if (is_array($values[$path]) || is_object($values[$path])) {
// value already processed by FormDisplay::save
$lines = array();
foreach ($values[$path] as $ip => $v) {
$v = Util::requestString($v);
$lines[] = preg_match('/^-\d+$/', $ip)
? $v
: $ip . ': ' . $v;
}
} else {
// AJAX validation
$lines = explode("\n", $values[$path]);
}
foreach ($lines as $line) {
$line = trim($line);
$matches = array();
// we catch anything that may (or may not) be an IP
if (!preg_match("/^(.+):(?:[ ]?)\\w+$/", $line, $matches)) {
$result[$path][] = __('Incorrect value:') . ' '
. htmlspecialchars($line);
continue;
}
// now let's check whether we really have an IP address
if (filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false
&& filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false
) {
$ip = htmlspecialchars(trim($matches[1]));
$result[$path][] = sprintf(__('Incorrect IP address: %s'), $ip);
continue;
}
}
return $result;
}
/**
* Tests integer value
*
* @param string $path path to config
* @param array $values config values
* @param bool $allow_neg allow negative values
* @param bool $allow_zero allow zero
* @param int $max_value max allowed value
* @param string $error_string error message key:
* $GLOBALS["strConfig$error_lang_key"]
*
* @return string empty string if test is successful
*/
public static function validateNumber(
$path,
$values,
$allow_neg,
$allow_zero,
$max_value,
$error_string
) {
if (empty($values[$path])) {
return '';
}
$value = Util::requestString($values[$path]);
if (intval($value) != $value
|| (! $allow_neg && $value < 0)
|| (! $allow_zero && $value == 0)
|| $value > $max_value
) {
return $error_string;
}
return '';
}
/**
* Validates port number
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validatePortNumber($path, $values)
{
return array(
$path => static::validateNumber(
$path,
$values,
false,
false,
65535,
__('Not a valid port number!')
)
);
}
/**
* Validates positive number
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validatePositiveNumber($path, $values)
{
return array(
$path => static::validateNumber(
$path,
$values,
false,
false,
PHP_INT_MAX,
__('Not a positive number!')
)
);
}
/**
* Validates non-negative number
*
* @param string $path path to config
* @param array $values config values
*
* @return array
*/
public static function validateNonNegativeNumber($path, $values)
{
return array(
$path => static::validateNumber(
$path,
$values,
false,
true,
PHP_INT_MAX,
__('Not a non-negative number!')
)
);
}
/**
* Validates value according to given regular expression
* Pattern and modifiers must be a valid for PCRE <b>and</b> JavaScript RegExp
*
* @param string $path path to config
* @param array $values config values
* @param string $regex regular expression to match
*
* @return array
*/
public static function validateByRegex($path, $values, $regex)
{
if (!isset($values[$path])) {
return '';
}
$result = preg_match($regex, Util::requestString($values[$path]));
return array($path => ($result ? '' : __('Incorrect value!')));
}
/**
* Validates upper bound for numeric inputs
*
* @param string $path path to config
* @param array $values config values
* @param int $max_value maximal allowed value
*
* @return array
*/
public static function validateUpperBound($path, $values, $max_value)
{
$result = $values[$path] <= $max_value;
return array($path => ($result ? ''
: sprintf(__('Value must be equal or lower than %s!'), $max_value)));
}
}

View File

@ -0,0 +1,53 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Common config manipulation functions
*
* @package PhpMyAdmin
*/
/**
* Returns sanitized language string, taking into account our special codes
* for formatting. Takes variable number of arguments.
* Based on PMA_sanitize from sanitize.lib.php.
*
* @param string $lang_key key in $GLOBALS WITHOUT 'strSetup' prefix
*
* @return string
*/
function PMA_lang($lang_key)
{
$message = isset($GLOBALS["strConfig$lang_key"])
? $GLOBALS["strConfig$lang_key"] : $lang_key;
$message = PMA_sanitize($message);
if (func_num_args() == 1) {
return $message;
} else {
$args = func_get_args();
array_shift($args);
return vsprintf($message, $args);
}
}
/**
* Returns translated field name/description or comment
*
* @param string $canonical_path path to handle
* @param string $type 'name', 'desc' or 'cmt'
* @param mixed $default default value
*
* @return string
*/
function PMA_langName($canonical_path, $type = 'name', $default = 'key')
{
$lang_key = str_replace(
array('Servers/1/', '/'),
array('Servers/', '_'),
$canonical_path
) . '_' . $type;
return isset($GLOBALS["strConfig$lang_key"])
? ($type == 'desc' ? PMA_lang($lang_key) : $GLOBALS["strConfig$lang_key"])
: ($default == 'key' ? $lang_key : $default);
}

View File

@ -0,0 +1,995 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Messages for phpMyAdmin.
*
* This file defines variables in a special format suited for the
* configuration subsystem, with $strConfig as a prefix, _desc or _name
* as a suffix, and the directive name in between.
*
* @package PhpMyAdmin
*/
if (!function_exists('__')) {
exit();
}
$strConfigAllowArbitraryServer_desc = __(
'If enabled, user can enter any MySQL server in login form for cookie auth.'
);
$strConfigAllowArbitraryServer_name = __('Allow login to any MySQL server');
$strConfigArbitraryServerRegexp_desc = __(
'Restricts the MySQL servers the user can enter when a login to an arbitrary '
. 'MySQL server is enabled by matching the IP or hostname of the MySQL server ' .
'to the given regular expression.'
);
$strConfigArbitraryServerRegexp_name = __('Restrict login to MySQL server');
$strConfigAllowThirdPartyFraming_desc = __(
'Enabling this allows a page located on a different domain to call phpMyAdmin '
. 'inside a frame, and is a potential [strong]security hole[/strong] allowing '
. 'cross-frame scripting (XSS) attacks.'
);
$strConfigAllowThirdPartyFraming_name = __('Allow third party framing');
$strConfigAllowUserDropDatabase_name
= __('Show "Drop database" link to normal users');
$strConfigblowfish_secret_desc = __(
'Secret passphrase used for encrypting cookies in [kbd]cookie[/kbd] '
. 'authentication.'
);
$strConfigblowfish_secret_name = __('Blowfish secret');
$strConfigBrowseMarkerEnable_desc = __('Highlight selected rows.');
$strConfigBrowseMarkerEnable_name = __('Row marker');
$strConfigBrowsePointerEnable_desc = __(
'Highlight row pointed by the mouse cursor.'
);
$strConfigBrowsePointerEnable_name = __('Highlight pointer');
$strConfigBZipDump_desc = __(
'Enable bzip2 compression for'
. ' import operations.'
);
$strConfigBZipDump_name = __('Bzip2');
$strConfigCharEditing_desc = __(
'Defines which type of editing controls should be used for CHAR and VARCHAR '
. 'columns; [kbd]input[/kbd] - allows limiting of input length, '
. '[kbd]textarea[/kbd] - allows newlines in columns.'
);
$strConfigCharEditing_name = __('CHAR columns editing');
$strConfigCodemirrorEnable_desc = __(
'Use user-friendly editor for editing SQL queries '
. '(CodeMirror) with syntax highlighting and '
. 'line numbers.'
);
$strConfigCodemirrorEnable_name = __('Enable CodeMirror');
$strConfigLintEnable_desc = __(
'Find any errors in the query before executing it.'
. ' Requires CodeMirror to be enabled.'
);
$strConfigLintEnable_name = __('Enable linter');
$strConfigMinSizeForInputField_desc = __(
'Defines the minimum size for input fields generated for CHAR and VARCHAR '
. 'columns.'
);
$strConfigMinSizeForInputField_name = __('Minimum size for input field');
$strConfigMaxSizeForInputField_desc = __(
'Defines the maximum size for input fields generated for CHAR and VARCHAR '
. 'columns.'
);
$strConfigMaxSizeForInputField_name = __('Maximum size for input field');
$strConfigCharTextareaCols_desc = __(
'Number of columns for CHAR/VARCHAR textareas.'
);
$strConfigCharTextareaCols_name = __('CHAR textarea columns');
$strConfigCharTextareaRows_desc = __('Number of rows for CHAR/VARCHAR textareas.');
$strConfigCharTextareaRows_name = __('CHAR textarea rows');
$strConfigCheckConfigurationPermissions_name = __('Check config file permissions');
$strConfigCompressOnFly_desc = __(
'Compress gzip exports on the fly without the need for much memory; if '
. 'you encounter problems with created gzip files disable this feature.'
);
$strConfigCompressOnFly_name = __('Compress on the fly');
$strConfigConfigurationFile = __('Configuration file');
$strConfigConfirm_desc = __(
'Whether a warning ("Are your really sure…") should be displayed '
. 'when you\'re about to lose data.'
);
$strConfigConfirm_name = __('Confirm DROP queries');
$strConfigDBG_sql_desc = __(
'Log SQL queries and their execution time, to be displayed in the console'
);
$strConfigDBG_sql_name = __('Debug SQL');
$strConfigDefaultTabDatabase_desc
= __('Tab that is displayed when entering a database.');
$strConfigDefaultTabDatabase_name = __('Default database tab');
$strConfigDefaultTabServer_desc = __(
'Tab that is displayed when entering a server.'
);
$strConfigDefaultTabServer_name = __('Default server tab');
$strConfigDefaultTabTable_desc = __('Tab that is displayed when entering a table.');
$strConfigDefaultTabTable_name = __('Default table tab');
$strConfigEnableAutocompleteForTablesAndColumns_desc = __(
'Autocomplete of the table and column names in the SQL queries.'
);
$strConfigEnableAutocompleteForTablesAndColumns_name = __(
'Enable autocomplete for table and column names'
);
$strConfigHideStructureActions_desc
= __('Whether the table structure actions should be hidden.');
$strConfigShowColumnComments_name = __('Show column comments');
$strConfigShowColumnComments_desc
= __('Whether column comments should be shown in table structure view');
$strConfigHideStructureActions_name = __('Hide table structure actions');
$strConfigDisplayServersList_desc
= __('Show server listing as a list instead of a drop down.');
$strConfigDisplayServersList_name = __('Display servers as a list');
$strConfigDisableMultiTableMaintenance_desc = __(
'Disable the table maintenance mass operations, like optimizing or repairing '
. 'the selected tables of a database.'
);
$strConfigDisableMultiTableMaintenance_name = __('Disable multi table maintenance');
$strConfigExecTimeLimit_desc = __(
'Set the number of seconds a script is allowed to run ([kbd]0[/kbd] for no '
. 'limit).'
);
$strConfigExecTimeLimit_name = __('Maximum execution time');
$strConfigExport_lock_tables_name = sprintf(
__('Use %s statement'), '<code>LOCK TABLES</code>'
);
$strConfigExport_asfile_name = __('Save as file');
$strConfigExport_charset_name = __('Character set of the file');
$strConfigExport_codegen_format_name = __('Format');
$strConfigExport_compression_name = __('Compression');
$strConfigExport_csv_columns_name = __('Put columns names in the first row');
$strConfigExport_csv_enclosed_name = __('Columns enclosed with');
$strConfigExport_csv_escaped_name = __('Columns escaped with');
$strConfigExport_csv_null_name = __('Replace NULL with');
$strConfigExport_csv_removeCRLF_name = __('Remove CRLF characters within columns');
$strConfigExport_csv_separator_name = __('Columns terminated with');
$strConfigExport_csv_terminated_name = __('Lines terminated with');
$strConfigExport_excel_columns_name = __('Put columns names in the first row');
$strConfigExport_excel_edition_name = __('Excel edition');
$strConfigExport_excel_null_name = __('Replace NULL with');
$strConfigExport_excel_removeCRLF_name = __('Remove CRLF characters within columns');
$strConfigExport_file_template_database_name = __('Database name template');
$strConfigExport_file_template_server_name = __('Server name template');
$strConfigExport_file_template_table_name = __('Table name template');
$strConfigExport_format_name = __('Format');
$strConfigExport_htmlword_columns_name = __('Put columns names in the first row');
$strConfigExport_htmlword_null_name = __('Replace NULL with');
$strConfigExport_htmlword_structure_or_data_name = __('Dump table');
$strConfigExport_latex_caption_name = __('Include table caption');
$strConfigExport_latex_columns_name = __('Put columns names in the first row');
$strConfigExport_latex_comments_name = __('Comments');
$strConfigExport_latex_data_caption_name = __('Table caption');
$strConfigExport_latex_data_continued_caption_name = __('Continued table caption');
$strConfigExport_latex_data_label_name = __('Label key');
$strConfigExport_latex_mime_name = __('MIME type');
$strConfigExport_latex_null_name = __('Replace NULL with');
$strConfigExport_latex_relation_name = __('Relations');
$strConfigExport_latex_structure_caption_name = __('Table caption');
$strConfigExport_latex_structure_continued_caption_name
= __('Continued table caption');
$strConfigExport_latex_structure_label_name = __('Label key');
$strConfigExport_latex_structure_or_data_name = __('Dump table');
$strConfigExport_method_name = __('Export method');
$strConfigExport_ods_columns_name = __('Put columns names in the first row');
$strConfigExport_ods_null_name = __('Replace NULL with');
$strConfigExport_odt_columns_name = __('Put columns names in the first row');
$strConfigExport_odt_comments_name = __('Comments');
$strConfigExport_odt_mime_name = __('MIME type');
$strConfigExport_odt_null_name = __('Replace NULL with');
$strConfigExport_odt_relation_name = __('Relations');
$strConfigExport_odt_structure_or_data_name = __('Dump table');
$strConfigExport_onserver_name = __('Save on server');
$strConfigExport_onserver_overwrite_name = __('Overwrite existing file(s)');
$strConfigExport_as_separate_files_name = __('Export as separate files');
$strConfigExport_quick_export_onserver_name = __('Save on server');
$strConfigExport_quick_export_onserver_overwrite_name
= __('Overwrite existing file(s)');
$strConfigExport_remember_file_template_name = __('Remember file name template');
$strConfigExport_sql_auto_increment_name = __('Add AUTO_INCREMENT value');
$strConfigExport_sql_backquotes_name
= __('Enclose table and column names with backquotes');
$strConfigExport_sql_compatibility_name = __('SQL compatibility mode');
$strConfigExport_sql_dates_name = __('Creation/Update/Check dates');
$strConfigExport_sql_delayed_name = __('Use delayed inserts');
$strConfigExport_sql_disable_fk_name = __('Disable foreign key checks');
$strConfigExport_sql_views_as_tables_name = __('Export views as tables');
$strConfigExport_sql_metadata_name = __(
'Export related metadata from phpMyAdmin configuration storage'
);
$strConfigExport_sql_create_database_name = sprintf(__('Add %s'), 'CREATE DATABASE / USE');
$strConfigExport_sql_drop_database_name = sprintf(__('Add %s'), 'DROP DATABASE');
$strConfigExport_sql_drop_table_name = sprintf(
__('Add %s'), 'DROP TABLE / VIEW / PROCEDURE / FUNCTION / EVENT / TRIGGER'
);
$strConfigExport_sql_create_table_name = sprintf(__('Add %s'), 'CREATE TABLE');
$strConfigExport_sql_create_view_name = sprintf(__('Add %s'), 'CREATE VIEW');
$strConfigExport_sql_create_trigger_name
= sprintf(__('Add %s'), 'CREATE TRIGGER');
$strConfigExport_sql_hex_for_binary_name = __('Use hexadecimal for BINARY & BLOB');
$strConfigExport_sql_if_not_exists_name = __(
'Add IF NOT EXISTS (less efficient as indexes will be generated during'
. ' table creation)'
);
$strConfigExport_sql_ignore_name = __('Use ignore inserts');
$strConfigExport_sql_include_comments_name = __('Comments');
$strConfigExport_sql_insert_syntax_name = __('Syntax to use when inserting data');
$strConfigExport_sql_max_query_size_name = __('Maximal length of created query');
$strConfigExport_sql_mime_name = __('MIME type');
$strConfigExport_sql_procedure_function_name
= sprintf(__('Add %s'), 'CREATE PROCEDURE / FUNCTION / EVENT');
$strConfigExport_sql_relation_name = __('Relations');
$strConfigExport_sql_structure_or_data_name = __('Dump table');
$strConfigExport_sql_type_name = __('Export type');
$strConfigExport_sql_use_transaction_name = __('Enclose export in a transaction');
$strConfigExport_sql_utc_time_name = __('Export time in UTC');
$strConfigExport_texytext_columns_name = __('Put columns names in the first row');
$strConfigExport_texytext_null_name = __('Replace NULL with');
$strConfigExport_texytext_structure_or_data_name = __('Dump table');
$strConfigExport_xls_columns_name = __('Put columns names in the first row');
$strConfigExport_xls_null_name = __('Replace NULL with');
$strConfigExport_xlsx_columns_name = __('Put columns names in the first row');
$strConfigExport_xlsx_null_name = __('Replace NULL with');
$strConfigForeignKeyDropdownOrder_desc = __(
'Sort order for items in a foreign-key dropdown box; [kbd]content[/kbd] is '
. 'the referenced data, [kbd]id[/kbd] is the key value.'
);
$strConfigForeignKeyDropdownOrder_name = __('Foreign key dropdown order');
$strConfigForeignKeyMaxLimit_desc
= __('A dropdown will be used if fewer items are present.');
$strConfigForeignKeyMaxLimit_name = __('Foreign key limit');
$strConfigDefaultForeignKeyChecks_desc = __(
'Default value for foreign key checks checkbox for some queries.'
);
$strConfigDefaultForeignKeyChecks_name = __('Foreign key checks');
$strConfigForm_Browse = __('Browse mode');
$strConfigForm_Browse_desc = __('Customize browse mode.');
$strConfigForm_CodeGen = 'CodeGen';
$strConfigForm_CodeGen_desc = __('Customize default options.');
$strConfigForm_Csv = __('CSV');
$strConfigForm_Csv_desc = __('Customize default options.');
$strConfigForm_Developer = __('Developer');
$strConfigForm_Developer_desc = __('Settings for phpMyAdmin developers.');
$strConfigForm_Edit = __('Edit mode');
$strConfigForm_Edit_desc = __('Customize edit mode.');
$strConfigForm_Export = __('Export');
$strConfigForm_Export_defaults = __('Export defaults');
$strConfigForm_Export_defaults_desc = __('Customize default export options.');
$strConfigForm_Features = __('Features');
$strConfigForm_General = __('General');
$strConfigForm_General_desc = __('Set some commonly used options.');
$strConfigForm_Import = __('Import');
$strConfigForm_Import_defaults = __('Import defaults');
$strConfigForm_Import_defaults_desc = __('Customize default common import options.');
$strConfigForm_Import_export = __('Import / export');
$strConfigForm_Import_export_desc
= __('Set import and export directories and compression options.');
$strConfigForm_Latex = __('LaTeX');
$strConfigForm_Latex_desc = __('Customize default options.');
$strConfigForm_Navi_databases = __('Databases');
$strConfigForm_Navi_databases_desc = __('Databases display options.');
$strConfigForm_Navi_panel = __('Navigation panel');
$strConfigForm_Navi_panel_desc = __('Customize appearance of the navigation panel.');
$strConfigForm_Navi_tree = __('Navigation tree');
$strConfigForm_Navi_tree_desc = __('Customize the navigation tree.');
$strConfigForm_Navi_servers = __('Servers');
$strConfigForm_Navi_servers_desc = __('Servers display options.');
$strConfigForm_Navi_tables = __('Tables');
$strConfigForm_Navi_tables_desc = __('Tables display options.');
$strConfigForm_Main_panel = __('Main panel');
$strConfigForm_Microsoft_Office = __('Microsoft Office');
$strConfigForm_Microsoft_Office_desc = __('Customize default options.');
$strConfigForm_Open_Document = 'OpenDocument';
$strConfigForm_Open_Document_desc = __('Customize default options.');
$strConfigForm_Other_core_settings = __('Other core settings');
$strConfigForm_Other_core_settings_desc
= __('Settings that didn\'t fit anywhere else.');
$strConfigForm_Page_titles = __('Page titles');
$strConfigForm_Page_titles_desc = __(
'Specify browser\'s title bar text. Refer to '
. '[doc@faq6-27]documentation[/doc] for magic strings that can be used '
. 'to get special values.'
);
$strConfigForm_Security = __('Security');
$strConfigForm_Security_desc = __(
'Please note that phpMyAdmin is just a user interface and its features do not '
. 'limit MySQL.'
);
$strConfigForm_Server = __('Basic settings');
$strConfigForm_Server_auth = __('Authentication');
$strConfigForm_Server_auth_desc = __('Authentication settings.');
$strConfigForm_Server_config = __('Server configuration');
$strConfigForm_Server_config_desc = __(
'Advanced server configuration, do not change these options unless you know '
. 'what they are for.'
);
$strConfigForm_Server_desc = __('Enter server connection parameters.');
$strConfigForm_Server_pmadb = __('Configuration storage');
$strConfigForm_Server_pmadb_desc = __(
'Configure phpMyAdmin configuration storage to gain access to additional '
. 'features, see [doc@linked-tables]phpMyAdmin configuration storage[/doc] in '
. 'documentation.'
);
$strConfigForm_Server_tracking = __('Changes tracking');
$strConfigForm_Server_tracking_desc = __(
'Tracking of changes made in database. Requires the phpMyAdmin configuration '
. 'storage.'
);
$strConfigFormset_Export = __('Customize export options');
$strConfigFormset_Features = __('Features');
$strConfigFormset_Import = __('Customize import defaults');
$strConfigFormset_Navi_panel = __('Customize navigation panel');
$strConfigFormset_Main_panel = __('Customize main panel');
$strConfigFormset_Sql_queries = __('SQL queries');
$strConfigForm_Sql = __('SQL');
$strConfigForm_Sql_box = __('SQL Query box');
$strConfigForm_Sql_box_desc = __('Customize links shown in SQL Query boxes.');
$strConfigForm_Sql_desc = __('Customize default options.');
$strConfigForm_Sql_queries = __('SQL queries');
$strConfigForm_Sql_queries_desc = __('SQL queries settings.');
$strConfigForm_Startup = __('Startup');
$strConfigForm_Startup_desc = __('Customize startup page.');
$strConfigForm_DbStructure = __('Database structure');
$strConfigForm_DbStructure_desc
= __('Choose which details to show in the database structure (list of tables).');
$strConfigForm_TableStructure = __('Table structure');
$strConfigForm_TableStructure_desc
= __('Settings for the table structure (list of columns).');
$strConfigForm_Tabs = __('Tabs');
$strConfigForm_Tabs_desc = __('Choose how you want tabs to work.');
$strConfigForm_DisplayRelationalSchema = __('Display relational schema');
$strConfigForm_DisplayRelationalSchema_desc = '';
$strConfigPDFDefaultPageSize_name = __('Paper size');
$strConfigPDFDefaultPageSize_desc = '';
$strConfigForm_Databases = __('Databases');
$strConfigForm_Text_fields = __('Text fields');
$strConfigForm_Text_fields_desc = __('Customize text input fields.');
$strConfigForm_Texy = __('Texy! text');
$strConfigForm_Texy_desc = __('Customize default options');
$strConfigForm_Warnings = __('Warnings');
$strConfigForm_Warnings_desc
= __('Disable some of the warnings shown by phpMyAdmin.');
$strConfigGZipDump_desc = __(
'Enable gzip compression for import '
. 'and export operations.'
);
$strConfigGZipDump_name = __('GZip');
$strConfigIconvExtraParams_name = __('Extra parameters for iconv');
$strConfigIgnoreMultiSubmitErrors_desc = __(
'If enabled, phpMyAdmin continues computing multiple-statement queries even if '
. 'one of the queries failed.'
);
$strConfigIgnoreMultiSubmitErrors_name = __('Ignore multiple statement errors');
$strConfigImport_allow_interrupt_desc = __(
'Allow interrupt of import in case script detects it is close to time limit. '
. 'This might be a good way to import large files, however it can break '
. 'transactions.'
);
$strConfigImport_allow_interrupt_name = __('Partial import: allow interrupt');
$strConfigImport_charset_name = __('Character set of the file');
$strConfigImport_csv_col_names_name = __('Lines terminated with');
$strConfigImport_csv_enclosed_name = __('Columns enclosed with');
$strConfigImport_csv_escaped_name = __('Columns escaped with');
$strConfigImport_csv_ignore_name = __('Do not abort on INSERT error');
$strConfigImport_csv_replace_name = __('Add ON DUPLICATE KEY UPDATE');
$strConfigImport_csv_replace_desc = __(
'Update data when duplicate keys found on import'
);
$strConfigImport_csv_terminated_name = __('Columns terminated with');
$strConfigImport_format_desc = __(
'Default format; be aware that this list depends on location (database, table) '
. 'and only SQL is always available.'
);
$strConfigImport_format_name = __('Format of imported file');
$strConfigImport_ldi_enclosed_name = __('Columns enclosed with');
$strConfigImport_ldi_escaped_name = __('Columns escaped with');
$strConfigImport_ldi_ignore_name = __('Do not abort on INSERT error');
$strConfigImport_ldi_local_option_name = __('Use LOCAL keyword');
$strConfigImport_ldi_replace_name = __('Add ON DUPLICATE KEY UPDATE');
$strConfigImport_ldi_replace_desc = __(
'Update data when duplicate keys found on import'
);
$strConfigImport_ldi_terminated_name = __('Columns terminated with');
$strConfigImport_ods_col_names_name = __('Column names in first row');
$strConfigImport_ods_empty_rows_name = __('Do not import empty rows');
$strConfigImport_ods_recognize_currency_name
= __('Import currencies ($5.00 to 5.00)');
$strConfigImport_ods_recognize_percentages_name
= __('Import percentages as proper decimals (12.00% to .12)');
$strConfigImport_skip_queries_desc = __('Number of queries to skip from start.');
$strConfigImport_skip_queries_name = __('Partial import: skip queries');
$strConfigImport_sql_compatibility_name = __('SQL compatibility mode');
$strConfigImport_sql_no_auto_value_on_zero_name
= __('Do not use AUTO_INCREMENT for zero values');
$strConfigImport_sql_read_as_multibytes_name = __('Read as multibytes');
$strConfigImport_xls_col_names_name = __('Column names in first row');
$strConfigImport_xlsx_col_names_name = __('Column names in first row');
$strConfigInitialSlidersState_name = __('Initial state for sliders');
$strConfigInsertRows_desc = __('How many rows can be inserted at one time.');
$strConfigInsertRows_name = __('Number of inserted rows');
$strConfigLimitChars_desc = __(
'Maximum number of characters shown in any non-numeric column on browse view.'
);
$strConfigLimitChars_name = __('Limit column characters');
$strConfigLoginCookieDeleteAll_desc = __(
'If TRUE, logout deletes cookies for all servers; when set to FALSE, logout '
. 'only occurs for the current server. Setting this to FALSE makes it easy to '
. 'forget to log out from other servers when connected to multiple servers.'
);
$strConfigLoginCookieDeleteAll_name = __('Delete all cookies on logout');
$strConfigLoginCookieRecall_desc = __(
'Define whether the previous login should be recalled or not in '
. '[kbd]cookie[/kbd] authentication mode.'
);
$strConfigLoginCookieRecall_name = __('Recall user name');
$strConfigLoginCookieStore_desc = __(
'Defines how long (in seconds) a login cookie should be stored in browser. '
. 'The default of 0 means that it will be kept for the existing session only, '
. 'and will be deleted as soon as you close the browser window. This is '
. 'recommended for non-trusted environments.'
);
$strConfigLoginCookieStore_name = __('Login cookie store');
$strConfigLoginCookieValidity_desc
= __('Define how long (in seconds) a login cookie is valid.');
$strConfigLoginCookieValidity_name = __('Login cookie validity');
$strConfigLongtextDoubleTextarea_desc
= __('Double size of textarea for LONGTEXT columns.');
$strConfigLongtextDoubleTextarea_name = __('Bigger textarea for LONGTEXT');
$strConfigMaxCharactersInDisplayedSQL_desc
= __('Maximum number of characters used when a SQL query is displayed.');
$strConfigMaxCharactersInDisplayedSQL_name = __('Maximum displayed SQL length');
$strConfigMaxDbList_cmt = __('Users cannot set a higher value');
$strConfigMaxDbList_desc
= __('Maximum number of databases displayed in database list.');
$strConfigMaxDbList_name = __('Maximum databases');
$strConfigFirstLevelNavigationItems_desc = __(
'The number of items that can be displayed on each page on the first level'
. ' of the navigation tree.'
);
$strConfigFirstLevelNavigationItems_name = __('Maximum items on first level');
$strConfigMaxNavigationItems_desc = __(
'The number of items that can be displayed on each page of the navigation tree.'
);
$strConfigMaxNavigationItems_name = __('Maximum items in branch');
$strConfigMaxRows_desc = __(
'Number of rows displayed when browsing a result set. If the result set '
. 'contains more rows, "Previous" and "Next" links will be '
. 'shown.'
);
$strConfigMaxRows_name = __('Maximum number of rows to display');
$strConfigMaxTableList_cmt = __('Users cannot set a higher value');
$strConfigMaxTableList_desc = __(
'Maximum number of tables displayed in table list.'
);
$strConfigMaxTableList_name = __('Maximum tables');
$strConfigMemoryLimit_desc = __(
'The number of bytes a script is allowed to allocate, eg. [kbd]32M[/kbd] '
. '([kbd]-1[/kbd] for no limit and [kbd]0[/kbd] for no change).'
);
$strConfigMemoryLimit_name = __('Memory limit');
$strConfigShowDatabasesNavigationAsTree_desc = __(
'In the navigation panel, replaces the database tree with a selector'
);
$strConfigShowDatabasesNavigationAsTree_name = __(
'Show databases navigation as tree'
);
$strConfigNavigationLinkWithMainPanel_desc = __(
'Link with main panel by highlighting the current database or table.'
);
$strConfigNavigationLinkWithMainPanel_name = __('Link with main panel');
$strConfigNavigationDisplayLogo_desc = __('Show logo in navigation panel.');
$strConfigNavigationDisplayLogo_name = __('Display logo');
$strConfigNavigationLogoLink_desc
= __('URL where logo in the navigation panel will point to.');
$strConfigNavigationLogoLink_name = __('Logo link URL');
$strConfigNavigationLogoLinkWindow_desc = __(
'Open the linked page in the main window ([kbd]main[/kbd]) or in a new one '
. '([kbd]new[/kbd]).'
);
$strConfigNavigationLogoLinkWindow_name = __('Logo link target');
$strConfigNavigationDisplayServers_desc
= __('Display server choice at the top of the navigation panel.');
$strConfigNavigationDisplayServers_name = __('Display servers selection');
$strConfigNavigationTreeDefaultTabTable_name = __('Target for quick access icon');
$strConfigNavigationTreeDefaultTabTable2_name = __(
'Target for second quick access icon'
);
$strConfigNavigationTreeDisplayItemFilterMinimum_desc = __(
'Defines the minimum number of items (tables, views, routines and events) to '
. 'display a filter box.'
);
$strConfigNavigationTreeDisplayItemFilterMinimum_name
= __('Minimum number of items to display the filter box');
$strConfigNavigationTreeDisplayDbFilterMinimum_name
= __('Minimum number of databases to display the database filter box');
$strConfigNavigationTreeEnableGrouping_desc = __(
'Group items in the navigation tree (determined by the separator defined in ' .
'the Databases and Tables tabs above).'
);
$strConfigNavigationTreeEnableGrouping_name = __('Group items in the tree');
$strConfigNavigationTreeDbSeparator_desc
= __('String that separates databases into different tree levels.');
$strConfigNavigationTreeDbSeparator_name = __('Database tree separator');
$strConfigNavigationTreeTableSeparator_desc
= __('String that separates tables into different tree levels.');
$strConfigNavigationTreeTableSeparator_name = __('Table tree separator');
$strConfigNavigationTreeTableLevel_name = __('Maximum table tree depth');
$strConfigNavigationTreePointerEnable_desc
= __('Highlight server under the mouse cursor.');
$strConfigNavigationTreePointerEnable_name = __('Enable highlighting');
$strConfigNavigationTreeEnableExpansion_desc = __(
'Whether to offer the possibility of tree expansion in the navigation panel.'
);
$strConfigNavigationTreeEnableExpansion_name
= __('Enable navigation tree expansion');
$strConfigNavigationTreeShowTables_name = __('Show tables in tree');
$strConfigNavigationTreeShowTables_desc
= __('Whether to show tables under database in the navigation tree');
$strConfigNavigationTreeShowViews_name = __('Show views in tree');
$strConfigNavigationTreeShowViews_desc
= __('Whether to show views under database in the navigation tree');
$strConfigNavigationTreeShowFunctions_name = __('Show functions in tree');
$strConfigNavigationTreeShowFunctions_desc
= __('Whether to show functions under database in the navigation tree');
$strConfigNavigationTreeShowProcedures_name = __('Show procedures in tree');
$strConfigNavigationTreeShowProcedures_desc
= __('Whether to show procedures under database in the navigation tree');
$strConfigNavigationTreeShowEvents_name = __('Show events in tree');
$strConfigNavigationTreeShowEvents_desc
= __('Whether to show events under database in the navigation tree');
$strConfigNumRecentTables_desc
= __('Maximum number of recently used tables; set 0 to disable.');
$strConfigNumFavoriteTables_desc
= __('Maximum number of favorite tables; set 0 to disable.');
$strConfigNumRecentTables_name = __('Recently used tables');
$strConfigNumFavoriteTables_name = __('Favorite tables');
$strConfigRowActionLinks_desc = __('These are Edit, Copy and Delete links.');
$strConfigRowActionLinks_name = __('Where to show the table row links');
$strConfigRowActionLinksWithoutUnique_desc = __(
'Whether to show row links even in the absence of a unique key.'
);
$strConfigRowActionLinksWithoutUnique_name = __('Show row links anyway');
$strConfigNaturalOrder_desc
= __('Use natural order for sorting table and database names.');
$strConfigNaturalOrder_name = __('Natural order');
$strConfigTableNavigationLinksMode_desc = __('Use only icons, only text or both.');
$strConfigTableNavigationLinksMode_name = __('Table navigation bar');
$strConfigOBGzip_desc
= __('Use GZip output buffering for increased speed in HTTP transfers.');
$strConfigOBGzip_name = __('GZip output buffering');
$strConfigOrder_desc = __(
'[kbd]SMART[/kbd] - i.e. descending order for columns of type TIME, DATE, '
. 'DATETIME and TIMESTAMP, ascending order otherwise.'
);
$strConfigOrder_name = __('Default sorting order');
$strConfigPersistentConnections_desc
= __('Use persistent connections to MySQL databases.');
$strConfigPersistentConnections_name = __('Persistent connections');
$strConfigPmaNoRelation_DisableWarning_desc = __(
'Disable the default warning that is displayed on the database details '
. 'Structure page if any of the required tables for the phpMyAdmin '
. 'configuration storage could not be found.'
);
$strConfigPmaNoRelation_DisableWarning_name
= __('Missing phpMyAdmin configuration storage tables');
$strConfigServerLibraryDifference_DisableWarning_desc = __(
'Disable the default warning that is displayed if a difference between the '
. 'MySQL library and server is detected.'
);
$strConfigServerLibraryDifference_DisableWarning_name
= __('Server/library difference warning');
$strConfigReservedWordDisableWarning_desc = __(
'Disable the default warning that is displayed on the Structure page if column '
. 'names in a table are reserved MySQL words.'
);
$strConfigReservedWordDisableWarning_name = __('MySQL reserved word warning');
$strConfigTabsMode_desc = __('Use only icons, only text or both.');
$strConfigTabsMode_name = __('How to display the menu tabs');
$strConfigActionLinksMode_desc = __('Use only icons, only text or both.');
$strConfigActionLinksMode_name = __('How to display various action links');
$strConfigProtectBinary_desc = __('Disallow BLOB and BINARY columns from editing.');
$strConfigProtectBinary_name = __('Protect binary columns');
$strConfigQueryHistoryDB_desc = __(
'Enable if you want DB-based query history (requires phpMyAdmin configuration '
. 'storage). If disabled, this utilizes JS-routines to display query history '
. '(lost by window close).'
);
$strConfigQueryHistoryDB_name = __('Permanent query history');
$strConfigQueryHistoryMax_cmt = __('Users cannot set a higher value');
$strConfigQueryHistoryMax_desc = __('How many queries are kept in history.');
$strConfigQueryHistoryMax_name = __('Query history length');
$strConfigRecodingEngine_desc
= __('Select which functions will be used for character set conversion.');
$strConfigRecodingEngine_name = __('Recoding engine');
$strConfigRememberSorting_desc
= __('When browsing tables, the sorting of each table is remembered.');
$strConfigRememberSorting_name = __('Remember table\'s sorting');
$strConfigTablePrimaryKeyOrder_desc = __(
'Default sort order for tables with a primary key.'
);
$strConfigTablePrimaryKeyOrder_name = __('Primary key default sort order');
$strConfigRepeatCells_desc
= __('Repeat the headers every X cells, [kbd]0[/kbd] deactivates this feature.');
$strConfigRepeatCells_name = __('Repeat headers');
$strConfigRestoreDefaultValue = __('Restore default value');
$strConfigGridEditing_name = __('Grid editing: trigger action');
$strConfigRelationalDisplay_name = __('Relational display');
$strConfigRelationalDisplay_desc = __('For display Options');
$strConfigSaveCellsAtOnce_name = __('Grid editing: save all edited cells at once');
$strConfigSaveDir_desc = __('Directory where exports can be saved on server.');
$strConfigSaveDir_name = __('Save directory');
$strConfigServers_AllowDeny_order_desc = __('Leave blank if not used.');
$strConfigServers_AllowDeny_order_name = __('Host authorization order');
$strConfigServers_AllowDeny_rules_desc = __('Leave blank for defaults.');
$strConfigServers_AllowDeny_rules_name = __('Host authorization rules');
$strConfigServers_AllowNoPassword_name = __('Allow logins without a password');
$strConfigServers_AllowRoot_name = __('Allow root login');
$strConfigServers_SessionTimeZone_name = __('Session timezone');
$strConfigServers_SessionTimeZone_desc = __(
'Sets the effective timezone; possibly different than the one from your ' .
'database server'
);
$strConfigServers_auth_http_realm_desc
= __('HTTP Basic Auth Realm name to display when doing HTTP Auth.');
$strConfigServers_auth_http_realm_name = __('HTTP Realm');
$strConfigServers_auth_type_desc = __('Authentication method to use.');
$strConfigServers_auth_type_name = __('Authentication type');
$strConfigServers_bookmarktable_desc = __(
'Leave blank for no [doc@bookmarks@]bookmark[/doc] '
. 'support, suggested: [kbd]pma__bookmark[/kbd]'
);
$strConfigServers_bookmarktable_name = __('Bookmark table');
$strConfigServers_column_info_desc = __(
'Leave blank for no column comments/mime types, suggested: '
. '[kbd]pma__column_info[/kbd].'
);
$strConfigServers_column_info_name = __('Column information table');
$strConfigServers_compress_desc = __('Compress connection to MySQL server.');
$strConfigServers_compress_name = __('Compress connection');
$strConfigServers_connect_type_desc
= __('How to connect to server, keep [kbd]tcp[/kbd] if unsure.');
$strConfigServers_connect_type_name = __('Connection type');
$strConfigServers_controlpass_name = __('Control user password');
$strConfigServers_controluser_desc = __(
'A special MySQL user configured with limited permissions, more information '
. 'available on [doc@linked-tables]documentation[/doc].'
);
$strConfigServers_controluser_name = __('Control user');
$strConfigServers_controlhost_desc = __(
'An alternate host to hold the configuration storage; leave blank to use the '
. 'already defined host.'
);
$strConfigServers_controlhost_name = __('Control host');
$strConfigServers_controlport_desc = __(
'An alternate port to connect to the host that holds the configuration storage; '
. 'leave blank to use the default port, or the already defined port, if the '
. 'controlhost equals host.'
);
$strConfigServers_controlport_name = __('Control port');
$strConfigServers_hide_db_desc
= __('Hide databases matching regular expression (PCRE).');
$strConfigServers_DisableIS_desc = __(
'More information on [a@https://sourceforge.net/p/phpmyadmin/bugs/2606/]PMA ' .
'bug tracker[/a] and [a@https://bugs.mysql.com/19588]MySQL Bugs[/a]'
);
$strConfigServers_DisableIS_name = __('Disable use of INFORMATION_SCHEMA');
$strConfigServers_hide_db_name = __('Hide databases');
$strConfigServers_history_desc = __(
'Leave blank for no SQL query history support, suggested: '
. '[kbd]pma__history[/kbd].'
);
$strConfigServers_history_name = __('SQL query history table');
$strConfigServers_host_desc = __('Hostname where MySQL server is running.');
$strConfigServers_host_name = __('Server hostname');
$strConfigServers_LogoutURL_name = __('Logout URL');
$strConfigServers_MaxTableUiprefs_desc = __(
'Limits number of table preferences which are stored in database, the oldest '
. 'records are automatically removed.'
);
$strConfigServers_MaxTableUiprefs_name
= __('Maximal number of table preferences to store');
$strConfigServers_savedsearches_name = __('QBE saved searches table');
$strConfigServers_savedsearches_desc = __(
'Leave blank for no QBE saved searches support, suggested: '
. '[kbd]pma__savedsearches[/kbd].'
);
$strConfigServers_export_templates_name = __('Export templates table');
$strConfigServers_export_templates_desc = __(
'Leave blank for no export template support, suggested: '
. '[kbd]pma__export_templates[/kbd].'
);
$strConfigServers_central_columns_name = __('Central columns table');
$strConfigServers_central_columns_desc = __(
'Leave blank for no central columns support, suggested: '
. '[kbd]pma__central_columns[/kbd].'
);
$strConfigServers_nopassword_desc = __('Try to connect without password.');
$strConfigServers_nopassword_name = __('Connect without password');
$strConfigServers_only_db_desc = __(
'You can use MySQL wildcard characters (% and _), escape them if you want to '
. 'use their literal instances, i.e. use [kbd]\'my\_db\'[/kbd] and not '
. '[kbd]\'my_db\'[/kbd].'
);
$strConfigServers_only_db_name = __('Show only listed databases');
$strConfigServers_password_desc = __('Leave empty if not using config auth.');
$strConfigServers_password_name = __('Password for config auth');
$strConfigServers_pdf_pages_desc = __(
'Leave blank for no PDF schema support, suggested: [kbd]pma__pdf_pages[/kbd].'
);
$strConfigServers_pdf_pages_name = __('PDF schema: pages table');
$strConfigServers_pmadb_desc = __(
'Database used for relations, bookmarks, and PDF features. See '
. '[doc@linked-tables]pmadb[/doc] for complete information. '
. 'Leave blank for no support. Suggested: [kbd]phpmyadmin[/kbd].'
);
$strConfigServers_pmadb_name = __('Database name');
$strConfigServers_port_desc
= __('Port on which MySQL server is listening, leave empty for default.');
$strConfigServers_port_name = __('Server port');
$strConfigServers_recent_desc = __(
'Leave blank for no "persistent" recently used tables across sessions, '
. 'suggested: [kbd]pma__recent[/kbd].'
);
$strConfigServers_recent_name = __('Recently used table');
$strConfigServers_favorite_desc = __(
'Leave blank for no "persistent" favorite tables across sessions, '
. 'suggested: [kbd]pma__favorite[/kbd].'
);
$strConfigServers_favorite_name = __('Favorites table');
$strConfigServers_relation_desc = __(
'Leave blank for no '
. '[doc@relations@]relation-links[/doc] support, '
. 'suggested: [kbd]pma__relation[/kbd].'
);
$strConfigServers_relation_name = __('Relation table');
$strConfigServers_SignonSession_desc = __(
'See [doc@authentication-modes]authentication '
. 'types[/doc] for an example.'
);
$strConfigServers_SignonSession_name = __('Signon session name');
$strConfigServers_SignonURL_name = __('Signon URL');
$strConfigServers_socket_desc
= __('Socket on which MySQL server is listening, leave empty for default.');
$strConfigServers_socket_name = __('Server socket');
$strConfigServers_ssl_desc = __('Enable SSL for connection to MySQL server.');
$strConfigServers_ssl_name = __('Use SSL');
$strConfigServers_table_coords_desc = __(
'Leave blank for no PDF schema support, suggested: [kbd]pma__table_coords[/kbd].'
);
$strConfigServers_table_coords_name = __(
'Designer and PDF schema: table coordinates'
);
$strConfigServers_table_info_desc = __(
'Table to describe the display columns, leave blank for no support; '
. 'suggested: [kbd]pma__table_info[/kbd].'
);
$strConfigServers_table_info_name = __('Display columns table');
$strConfigServers_table_uiprefs_desc = __(
'Leave blank for no "persistent" tables\' UI preferences across sessions, '
. 'suggested: [kbd]pma__table_uiprefs[/kbd].'
);
$strConfigServers_table_uiprefs_name = __('UI preferences table');
$strConfigServers_tracking_add_drop_database_desc = __(
'Whether a DROP DATABASE IF EXISTS statement will be added as first line to '
. 'the log when creating a database.'
);
$strConfigServers_tracking_add_drop_database_name = __('Add DROP DATABASE');
$strConfigServers_tracking_add_drop_table_desc = __(
'Whether a DROP TABLE IF EXISTS statement will be added as first line to the '
. 'log when creating a table.'
);
$strConfigServers_tracking_add_drop_table_name = __('Add DROP TABLE');
$strConfigServers_tracking_add_drop_view_desc = __(
'Whether a DROP VIEW IF EXISTS statement will be added as first line to the '
. 'log when creating a view.'
);
$strConfigServers_tracking_add_drop_view_name = __('Add DROP VIEW');
$strConfigServers_tracking_default_statements_desc
= __('Defines the list of statements the auto-creation uses for new versions.');
$strConfigServers_tracking_default_statements_name = __('Statements to track');
$strConfigServers_tracking_desc = __(
'Leave blank for no SQL query tracking support, suggested: '
. '[kbd]pma__tracking[/kbd].'
);
$strConfigServers_tracking_name = __('SQL query tracking table');
$strConfigServers_tracking_version_auto_create_desc = __(
'Whether the tracking mechanism creates versions for tables and views '
. 'automatically.'
);
$strConfigServers_tracking_version_auto_create_name
= __('Automatically create versions');
$strConfigServers_userconfig_desc = __(
'Leave blank for no user preferences storage in database, suggested: ' .
'[kbd]pma__userconfig[/kbd].'
);
$strConfigServers_userconfig_name = __('User preferences storage table');
$strConfigServers_users_desc = __(
'Both this table and the user groups table are required to enable the ' .
'configurable menus feature; leaving either one of them blank will disable ' .
'this feature, suggested: [kbd]pma__users[/kbd].'
);
$strConfigServers_users_name = __('Users table');
$strConfigServers_usergroups_desc = __(
'Both this table and the users table are required to enable the configurable ' .
'menus feature; leaving either one of them blank will disable this feature, ' .
'suggested: [kbd]pma__usergroups[/kbd].'
);
$strConfigServers_usergroups_name = __('User groups table');
$strConfigServers_navigationhiding_desc = __(
'Leave blank to disable the feature to hide and show navigation items, ' .
'suggested: [kbd]pma__navigationhiding[/kbd].'
);
$strConfigServers_navigationhiding_name = __('Hidden navigation items table');
$strConfigServers_user_desc = __('Leave empty if not using config auth.');
$strConfigServers_user_name = __('User for config auth');
$strConfigServers_verbose_desc = __(
'A user-friendly description of this server. Leave blank to display the ' .
'hostname instead.'
);
$strConfigServers_verbose_name = __('Verbose name of this server');
$strConfigShowAll_desc = __(
'Whether a user should be displayed a "show all (rows)" button.'
);
$strConfigShowAll_name = __('Allow to display all the rows');
$strConfigShowChgPassword_desc = __(
'Please note that enabling this has no effect with [kbd]config[/kbd] ' .
'authentication mode because the password is hard coded in the configuration ' .
'file; this does not limit the ability to execute the same command directly.'
);
$strConfigShowChgPassword_name = __('Show password change form');
$strConfigShowCreateDb_name = __('Show create database form');
$strConfigShowDbStructureComment_desc = __(
'Show or hide a column displaying the comments for all tables.'
);
$strConfigShowDbStructureComment_name = __('Show table comments');
$strConfigShowDbStructureCreation_desc = __(
'Show or hide a column displaying the Creation timestamp for all tables.'
);
$strConfigShowDbStructureCreation_name = __('Show creation timestamp');
$strConfigShowDbStructureLastUpdate_desc = __(
'Show or hide a column displaying the Last update timestamp for all tables.'
);
$strConfigShowDbStructureLastUpdate_name = __('Show last update timestamp');
$strConfigShowDbStructureLastCheck_desc = __(
'Show or hide a column displaying the Last check timestamp for all tables.'
);
$strConfigShowDbStructureLastCheck_name = __('Show last check timestamp');
$strConfigShowFieldTypesInDataEditView_desc = __(
'Defines whether or not type fields should be initially displayed in ' .
'edit/insert mode.'
);
$strConfigShowFieldTypesInDataEditView_name = __('Show field types');
$strConfigShowFunctionFields_desc = __(
'Display the function fields in edit/insert mode.'
);
$strConfigShowFunctionFields_name = __('Show function fields');
$strConfigShowHint_desc = __('Whether to show hint or not.');
$strConfigShowHint_name = __('Show hint');
$strConfigShowServerInfo_name = __('Show detailed MySQL server information');
$strConfigShowSQL_desc = __(
'Defines whether SQL queries generated by phpMyAdmin should be displayed.'
);
$strConfigShowSQL_name = __('Show SQL queries');
$strConfigRetainQueryBox_desc = __(
'Defines whether the query box should stay on-screen after its submission.'
);
$strConfigRetainQueryBox_name = __('Retain query box');
$strConfigShowStats_desc = __(
'Allow to display database and table statistics (eg. space usage).'
);
$strConfigShowStats_name = __('Show statistics');
$strConfigSkipLockedTables_desc = __(
'Mark used tables and make it possible to show databases with locked tables.'
);
$strConfigSkipLockedTables_name = __('Skip locked tables');
$strConfigSQLQuery_Edit_name = __('Edit');
$strConfigSQLQuery_Explain_name = __('Explain SQL');
$strConfigSQLQuery_Refresh_name = __('Refresh');
$strConfigSQLQuery_ShowAsPHP_name = __('Create PHP code');
$strConfigSuhosinDisableWarning_desc = __(
'Disable the default warning that is displayed on the main page if Suhosin is ' .
'detected.'
);
$strConfigSuhosinDisableWarning_name = __('Suhosin warning');
$strConfigLoginCookieValidityDisableWarning_desc = __(
'Disable the default warning that is displayed on the main page if the value ' .
'of the PHP setting session.gc_maxlifetime is less than the value of ' .
'`LoginCookieValidity`.'
);
$strConfigLoginCookieValidityDisableWarning_name = __(
'Login cookie validity warning'
);
$strConfigTextareaCols_desc = __(
'Textarea size (columns) in edit mode, this value will be emphasized for SQL ' .
'query textareas (*2).'
);
$strConfigTextareaCols_name = __('Textarea columns');
$strConfigTextareaRows_desc = __(
'Textarea size (rows) in edit mode, this value will be emphasized for SQL ' .
'query textareas (*2).'
);
$strConfigTextareaRows_name = __('Textarea rows');
$strConfigTitleDatabase_desc = __(
'Title of browser window when a database is selected.'
);
$strConfigTitleDatabase_name = __('Database');
$strConfigTitleDefault_desc = __(
'Title of browser window when nothing is selected.'
);
$strConfigTitleDefault_name = __('Default title');
$strConfigTitleServer_desc = __(
'Title of browser window when a server is selected.'
);
$strConfigTitleServer_name = __('Server');
$strConfigTitleTable_desc = __('Title of browser window when a table is selected.');
$strConfigTitleTable_name = __('Table');
$strConfigTrustedProxies_desc = __(
'Input proxies as [kbd]IP: trusted HTTP header[/kbd]. The following example ' .
'specifies that phpMyAdmin should trust a HTTP_X_FORWARDED_FOR ' .
'(X-Forwarded-For) header coming from the proxy 1.2.3.4:[br][kbd]1.2.3.4: ' .
'HTTP_X_FORWARDED_FOR[/kbd].'
);
$strConfigTrustedProxies_name = __('List of trusted proxies for IP allow/deny');
$strConfigUploadDir_desc = __(
'Directory on server where you can upload files for import.'
);
$strConfigUploadDir_name = __('Upload directory');
$strConfigUseDbSearch_desc = __('Allow for searching inside the entire database.');
$strConfigUseDbSearch_name = __('Use database search');
$strConfigUserprefsDeveloperTab_desc = __(
'When disabled, users cannot set any of the options below, regardless of the ' .
'checkbox on the right.'
);
$strConfigUserprefsDeveloperTab_name = __('Enable the Developer tab in settings');
$strConfigVersionCheckLink = __('Check for latest version');
$strConfigVersionCheck_desc = __(
'Enables check for latest version on main phpMyAdmin page.'
);
$strConfigVersionCheck_name = __('Version check');
$strConfigProxyUrl_desc = __(
'The url of the proxy to be used when retrieving the information about the ' .
'latest version of phpMyAdmin or when submitting error reports. You need this ' .
'if the server where phpMyAdmin is installed does not have direct access to ' .
'the internet. The format is: "hostname:portnumber".'
);
$strConfigProxyUrl_name = __('Proxy url');
$strConfigProxyUser_desc = __(
'The username for authenticating with the proxy. By default, no ' .
'authentication is performed. If a username is supplied, Basic ' .
'Authentication will be performed. No other types of authentication are ' .
'currently supported.'
);
$strConfigProxyUser_name = __('Proxy username');
$strConfigProxyPass_desc = __('The password for authenticating with the proxy.');
$strConfigProxyPass_name = __('Proxy password');
$strConfigZipDump_desc = __(
'Enable ZIP ' .
'compression for import and export operations.'
);
$strConfigZipDump_name = __('ZIP');
$strConfigCaptchaLoginPublicKey_desc = __(
'Enter your public key for your domain reCaptcha service.'
);
$strConfigCaptchaLoginPublicKey_name = __('Public key for reCaptcha');
$strConfigCaptchaLoginPrivateKey_desc = __(
'Enter your private key for your domain reCaptcha service.'
);
$strConfigCaptchaLoginPrivateKey_name = __('Private key for reCaptcha');
$strConfigSendErrorReports_desc = __(
'Choose the default action when sending error reports.'
);
$strConfigSendErrorReports_name = __('Send error reports');
$strConfigConsoleEnterExecutes_desc = __(
'Queries are executed by pressing Enter (instead of Ctrl+Enter). New lines ' .
'will be inserted with Shift+Enter.'
);
$strConfigConsoleEnterExecutes_name = __('Enter executes queries in console');
$strConfigZeroConf_desc = __(
'Enable Zero Configuration mode which lets you setup phpMyAdmin '
. 'configuration storage tables automatically.'
);
$strConfigZeroConf_name = __('Enable Zero Configuration mode');

View File

@ -0,0 +1,29 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Used for page-related settings
*
* Extends groups defined in user_preferences.forms.php
* specific to page-related settings
*
* See more info in user_preferences.forms.php
*
* @package PhpMyAdmin
*/
if (!is_array($forms)) {
$forms = array();
}
$forms['Browse'] = array();
$forms['Browse']['Browse'] = $forms['Main_panel']['Browse'];
$forms['DbStructure'] = array();
$forms['DbStructure']['DbStructure'] = $forms['Main_panel']['DbStructure'];
$forms['Edit'] = array();
$forms['Edit']['Edit'] = $forms['Main_panel']['Edit'];
$forms['Edit']['Text_fields'] = $forms['Features']['Text_fields'];
$forms['TableStructure'] = array();
$forms['TableStructure']['TableStructure'] = $forms['Main_panel']['TableStructure'];

View File

@ -0,0 +1,395 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* List of available forms, each form is described as an array of fields to display.
* Fields MUST have their counterparts in the $cfg array.
*
* There are two possible notations:
* $forms['Form group']['Form name'] = array('Servers' => array(1 => array('host')));
* can be written as
* $forms['Form group']['Form name'] = array('Servers/1/host');
*
* You can assign default values set by special button ("set value: ..."), eg.:
* 'Servers/1/pmadb' => 'phpmyadmin'
*
* To group options, use:
* ':group:' . __('group name') // just define a group
* or
* 'option' => ':group' // group starting from this option
* End group blocks with:
* ':group:end'
*
* @package PhpMyAdmin-Setup
*/
$forms = array();
$forms['_config.php'] = array(
'DefaultLang',
'ServerDefault');
$forms['Servers']['Server'] = array('Servers' => array(1 => array(
'verbose',
'host',
'port',
'socket',
'ssl',
'connect_type',
'compress',
'nopassword')));
$forms['Servers']['Server_auth'] = array('Servers' => array(1 => array(
'auth_type',
':group:' . __('Config authentication'),
'user',
'password',
':group:end',
':group:' . __('HTTP authentication'),
'auth_http_realm',
':group:end',
':group:' . __('Signon authentication'),
'SignonSession',
'SignonURL',
'LogoutURL')));
$forms['Servers']['Server_config'] = array('Servers' => array(1 => array(
'only_db',
'hide_db',
'AllowRoot',
'AllowNoPassword',
'DisableIS',
'AllowDeny/order',
'AllowDeny/rules',
'SessionTimeZone')));
$forms['Servers']['Server_pmadb'] = array('Servers' => array(1 => array(
'pmadb' => 'phpmyadmin',
'controlhost',
'controlport',
'controluser',
'controlpass',
'bookmarktable' => 'pma__bookmark',
'relation' => 'pma__relation',
'userconfig' => 'pma__userconfig',
'users' => 'pma__users',
'usergroups' => 'pma__usergroups',
'navigationhiding' => 'pma__navigationhiding',
'table_info' => 'pma__table_info',
'column_info' => 'pma__column_info',
'history' => 'pma__history',
'recent' => 'pma__recent',
'favorite' => 'pma__favorite',
'table_uiprefs' => 'pma__table_uiprefs',
'tracking' => 'pma__tracking',
'table_coords' => 'pma__table_coords',
'pdf_pages' => 'pma__pdf_pages',
'savedsearches' => 'pma__savedsearches',
'central_columns' => 'pma__central_columns',
'designer_settings' => 'pma__designer_settings',
'export_templates' => 'pma__export_templates',
'MaxTableUiprefs' => 100)));
$forms['Servers']['Server_tracking'] = array('Servers' => array(1 => array(
'tracking_version_auto_create',
'tracking_default_statements',
'tracking_add_drop_view',
'tracking_add_drop_table',
'tracking_add_drop_database',
)));
$forms['Features']['Import_export'] = array(
'UploadDir',
'SaveDir',
'RecodingEngine' => ':group',
'IconvExtraParams',
':group:end',
'ZipDump',
'GZipDump',
'BZipDump',
'CompressOnFly');
$forms['Features']['Security'] = array(
'blowfish_secret',
'CheckConfigurationPermissions',
'TrustedProxies',
'AllowUserDropDatabase',
'AllowArbitraryServer',
'ArbitraryServerRegexp',
'LoginCookieRecall',
'LoginCookieValidity',
'LoginCookieStore',
'LoginCookieDeleteAll',
'CaptchaLoginPublicKey',
'CaptchaLoginPrivateKey');
$forms['Features']['Page_titles'] = array(
'TitleDefault',
'TitleTable',
'TitleDatabase',
'TitleServer');
$forms['Features']['Warnings'] = array(
'ServerLibraryDifference_DisableWarning',
'PmaNoRelation_DisableWarning',
'SuhosinDisableWarning',
'LoginCookieValidityDisableWarning');
$forms['Features']['Developer'] = array(
'UserprefsDeveloperTab',
'DBG/sql');
$forms['Features']['Other_core_settings'] = array(
'NaturalOrder',
'InitialSlidersState',
'MaxDbList',
'MaxTableList',
'NumFavoriteTables',
'ShowHint',
'OBGzip',
'PersistentConnections',
'ExecTimeLimit',
'MemoryLimit',
'SkipLockedTables',
'DisableMultiTableMaintenance',
'UseDbSearch',
'VersionCheck',
'SendErrorReports',
'ConsoleEnterExecutes',
'ProxyUrl',
'ProxyUser',
'ProxyPass',
'AllowThirdPartyFraming',
'ZeroConf'
);
$forms['Sql_queries']['Sql_queries'] = array(
'ShowSQL',
'Confirm',
'QueryHistoryDB',
'QueryHistoryMax',
'IgnoreMultiSubmitErrors',
'MaxCharactersInDisplayedSQL',
'RetainQueryBox',
'CodemirrorEnable',
'LintEnable',
'EnableAutocompleteForTablesAndColumns',
'DefaultForeignKeyChecks');
$forms['Sql_queries']['Sql_box'] = array('SQLQuery' => array(
'Edit',
'Explain',
'ShowAsPHP',
'Refresh'));
$forms['Navi_panel']['Navi_panel'] = array(
'ShowDatabasesNavigationAsTree',
'NavigationLinkWithMainPanel',
'NavigationDisplayLogo',
'NavigationLogoLink',
'NavigationLogoLinkWindow',
'NavigationTreePointerEnable',
'FirstLevelNavigationItems',
'NavigationTreeDisplayItemFilterMinimum',
'NumRecentTables',
'NumFavoriteTables'
);
$forms['Navi_panel']['Navi_tree'] = array(
'MaxNavigationItems',
'NavigationTreeEnableGrouping',
'NavigationTreeEnableExpansion',
'NavigationTreeShowTables',
'NavigationTreeShowViews',
'NavigationTreeShowFunctions',
'NavigationTreeShowProcedures',
'NavigationTreeShowEvents'
);
$forms['Navi_panel']['Navi_servers'] = array(
'NavigationDisplayServers',
'DisplayServersList');
$forms['Navi_panel']['Navi_databases'] = array(
'NavigationTreeDbSeparator');
$forms['Navi_panel']['Navi_tables'] = array(
'NavigationTreeDefaultTabTable',
'NavigationTreeDefaultTabTable2',
'NavigationTreeTableSeparator',
'NavigationTreeTableLevel',
);
$forms['Main_panel']['Startup'] = array(
'ShowCreateDb',
'ShowStats',
'ShowServerInfo',
'ShowChgPassword');
$forms['Main_panel']['DbStructure'] = array(
'ShowDbStructureComment',
'ShowDbStructureCreation',
'ShowDbStructureLastUpdate',
'ShowDbStructureLastCheck');
$forms['Main_panel']['TableStructure'] = array(
'HideStructureActions',
'ShowColumnComments');
$forms['Main_panel']['Browse'] = array(
'TableNavigationLinksMode',
'ShowAll',
'MaxRows',
'Order',
'BrowsePointerEnable',
'BrowseMarkerEnable',
'GridEditing',
'SaveCellsAtOnce',
'RepeatCells',
'LimitChars',
'RowActionLinks',
'RowActionLinksWithoutUnique',
'TablePrimaryKeyOrder',
'RememberSorting',
'RelationalDisplay');
$forms['Main_panel']['Edit'] = array(
'ProtectBinary',
'ShowFunctionFields',
'ShowFieldTypesInDataEditView',
'CharEditing',
'MinSizeForInputField',
'MaxSizeForInputField',
'CharTextareaCols',
'CharTextareaRows',
'TextareaCols',
'TextareaRows',
'LongtextDoubleTextarea',
'InsertRows',
'ForeignKeyDropdownOrder',
'ForeignKeyMaxLimit');
$forms['Main_panel']['Tabs'] = array(
'TabsMode',
'ActionLinksMode',
'DefaultTabServer',
'DefaultTabDatabase',
'DefaultTabTable'
);
$forms['Import']['Import_defaults'] = array('Import' => array(
'format',
'charset',
'allow_interrupt',
'skip_queries'));
$forms['Import']['Sql'] = array('Import' => array(
'sql_compatibility',
'sql_no_auto_value_on_zero'));
$forms['Import']['Csv'] = array('Import' => array(
':group:' . __('CSV'),
'csv_replace',
'csv_ignore',
'csv_terminated',
'csv_enclosed',
'csv_escaped',
'csv_col_names',
':group:end',
':group:' . __('CSV using LOAD DATA'),
'ldi_replace',
'ldi_ignore',
'ldi_terminated',
'ldi_enclosed',
'ldi_escaped',
'ldi_local_option',
':group:end'));
$forms['Import']['Open_Document'] = array('Import' => array(
':group:' . __('OpenDocument Spreadsheet'),
'ods_col_names',
'ods_empty_rows',
'ods_recognize_percentages',
'ods_recognize_currency'));
$forms['Export']['Export_defaults'] = array('Export' => array(
'method',
':group:' . __('Quick'),
'quick_export_onserver',
'quick_export_onserver_overwrite',
':group:end',
':group:' . __('Custom'),
'format',
'compression',
'charset',
'lock_tables',
'as_separate_files',
'asfile' => ':group',
'onserver',
'onserver_overwrite',
':group:end',
'remember_file_template',
'file_template_table',
'file_template_database',
'file_template_server'));
$forms['Export']['Sql'] = array('Export' => array(
'sql_include_comments' => ':group',
'sql_dates',
'sql_relation',
'sql_mime',
':group:end',
'sql_use_transaction',
'sql_disable_fk',
'sql_views_as_tables',
'sql_metadata',
'sql_compatibility',
'sql_structure_or_data',
':group:' . __('Structure'),
'sql_drop_database',
'sql_create_database',
'sql_drop_table',
'sql_procedure_function',
'sql_create_table' => ':group',
'sql_if_not_exists',
'sql_auto_increment',
':group:end',
'sql_create_view',
'sql_create_trigger',
'sql_backquotes',
':group:end',
':group:' . __('Data'),
'sql_delayed',
'sql_ignore',
'sql_type',
'sql_insert_syntax',
'sql_max_query_size',
'sql_hex_for_binary',
'sql_utc_time'));
$forms['Export']['CodeGen'] = array('Export' => array(
'codegen_format'));
$forms['Export']['Csv'] = array('Export' => array(
':group:' . __('CSV'),
'csv_separator',
'csv_enclosed',
'csv_escaped',
'csv_terminated',
'csv_null',
'csv_removeCRLF',
'csv_columns',
':group:end',
':group:' . __('CSV for MS Excel'),
'excel_null',
'excel_removeCRLF',
'excel_columns',
'excel_edition'));
$forms['Export']['Latex'] = array('Export' => array(
'latex_caption',
'latex_structure_or_data',
':group:' . __('Structure'),
'latex_structure_caption',
'latex_structure_continued_caption',
'latex_structure_label',
'latex_relation',
'latex_comments',
'latex_mime',
':group:end',
':group:' . __('Data'),
'latex_columns',
'latex_data_caption',
'latex_data_continued_caption',
'latex_data_label',
'latex_null'));
$forms['Export']['Microsoft_Office'] = array('Export' => array(
':group:' . __('Microsoft Word 2000'),
'htmlword_structure_or_data',
'htmlword_null',
'htmlword_columns'));
$forms['Export']['Open_Document'] = array('Export' => array(
':group:' . __('OpenDocument Spreadsheet'),
'ods_columns',
'ods_null',
':group:end',
':group:' . __('OpenDocument Text'),
'odt_structure_or_data',
':group:' . __('Structure'),
'odt_relation',
'odt_comments',
'odt_mime',
':group:end',
':group:' . __('Data'),
'odt_columns',
'odt_null'));
$forms['Export']['Texy'] = array('Export' => array(
'texytext_structure_or_data',
':group:' . __('Data'),
'texytext_null',
'texytext_columns'));

View File

@ -0,0 +1,298 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* List of available forms, each form is described as an array of fields to display.
* Fields MUST have their counterparts in the $cfg array.
*
* To define form field, use the notation below:
* $forms['Form group']['Form name'] = array('Option/path');
*
* You can assign default values set by special button ("set value: ..."), eg.:
* 'Servers/1/pmadb' => 'phpmyadmin'
*
* To group options, use:
* ':group:' . __('group name') // just define a group
* or
* 'option' => ':group' // group starting from this option
* End group blocks with:
* ':group:end'
*
* @package PhpMyAdmin
*/
$forms = array();
$forms['Features']['General'] = array(
'VersionCheck',
'NaturalOrder',
'InitialSlidersState',
'LoginCookieValidity',
'SkipLockedTables',
'DisableMultiTableMaintenance',
'MaxTableList',
'ShowHint',
'SendErrorReports',
'ConsoleEnterExecutes'
);
$forms['Features']['Databases'] = array(
'Servers/1/only_db', // saves to Server/only_db
'Servers/1/hide_db', // saves to Server/hide_db
'MaxDbList'
);
$forms['Features']['Text_fields'] = array(
'CharEditing',
'MinSizeForInputField',
'MaxSizeForInputField',
'CharTextareaCols',
'CharTextareaRows',
'TextareaCols',
'TextareaRows',
'LongtextDoubleTextarea');
$forms['Features']['Page_titles'] = array(
'TitleDefault',
'TitleTable',
'TitleDatabase',
'TitleServer');
$forms['Features']['Warnings'] = array(
'ServerLibraryDifference_DisableWarning',
'PmaNoRelation_DisableWarning',
'SuhosinDisableWarning',
'LoginCookieValidityDisableWarning',
'ReservedWordDisableWarning');
// settings from this form are treated specially,
// see prefs_forms.php and user_preferences.lib.php
$forms['Features']['Developer'] = array(
'DBG/sql');
$forms['Sql_queries']['Sql_queries'] = array(
'ShowSQL',
'Confirm',
'QueryHistoryMax',
'IgnoreMultiSubmitErrors',
'MaxCharactersInDisplayedSQL',
'RetainQueryBox',
'CodemirrorEnable',
'LintEnable',
'EnableAutocompleteForTablesAndColumns',
'DefaultForeignKeyChecks');
$forms['Sql_queries']['Sql_box'] = array(
'SQLQuery/Edit',
'SQLQuery/Explain',
'SQLQuery/ShowAsPHP',
'SQLQuery/Refresh');
$forms['Navi_panel']['Navi_panel'] = array(
'ShowDatabasesNavigationAsTree',
'NavigationLinkWithMainPanel',
'NavigationDisplayLogo',
'NavigationLogoLink',
'NavigationLogoLinkWindow',
'NavigationTreePointerEnable',
'FirstLevelNavigationItems',
'NavigationTreeDisplayItemFilterMinimum',
'NumRecentTables',
'NumFavoriteTables'
);
$forms['Navi_panel']['Navi_tree'] = array(
'MaxNavigationItems',
'NavigationTreeEnableGrouping',
'NavigationTreeEnableExpansion',
'NavigationTreeShowTables',
'NavigationTreeShowViews',
'NavigationTreeShowFunctions',
'NavigationTreeShowProcedures',
'NavigationTreeShowEvents'
);
$forms['Navi_panel']['Navi_databases'] = array(
'NavigationTreeDisplayDbFilterMinimum',
'NavigationTreeDbSeparator');
$forms['Navi_panel']['Navi_tables'] = array(
'NavigationTreeDefaultTabTable',
'NavigationTreeDefaultTabTable2',
'NavigationTreeTableSeparator',
'NavigationTreeTableLevel',
);
$forms['Main_panel']['Startup'] = array(
'ShowCreateDb',
'ShowStats',
'ShowServerInfo');
$forms['Main_panel']['DbStructure'] = array(
'ShowDbStructureComment',
'ShowDbStructureCreation',
'ShowDbStructureLastUpdate',
'ShowDbStructureLastCheck');
$forms['Main_panel']['TableStructure'] = array(
'HideStructureActions',
'ShowColumnComments');
$forms['Main_panel']['Browse'] = array(
'TableNavigationLinksMode',
'ActionLinksMode',
'ShowAll',
'MaxRows',
'Order',
'BrowsePointerEnable',
'BrowseMarkerEnable',
'GridEditing',
'SaveCellsAtOnce',
'RepeatCells',
'LimitChars',
'RowActionLinks',
'RowActionLinksWithoutUnique',
'TablePrimaryKeyOrder',
'RememberSorting',
'RelationalDisplay');
$forms['Main_panel']['Edit'] = array(
'ProtectBinary',
'ShowFunctionFields',
'ShowFieldTypesInDataEditView',
'InsertRows',
'ForeignKeyDropdownOrder',
'ForeignKeyMaxLimit');
$forms['Main_panel']['Tabs'] = array(
'TabsMode',
'DefaultTabServer',
'DefaultTabDatabase',
'DefaultTabTable');
$forms['Main_panel']['DisplayRelationalSchema'] = array(
'PDFDefaultPageSize');
$forms['Import']['Import_defaults'] = array(
'Import/format',
'Import/charset',
'Import/allow_interrupt',
'Import/skip_queries'
);
$forms['Import']['Sql'] = array(
'Import/sql_compatibility',
'Import/sql_no_auto_value_on_zero',
'Import/sql_read_as_multibytes');
$forms['Import']['Csv'] = array(
':group:' . __('CSV'),
'Import/csv_replace',
'Import/csv_ignore',
'Import/csv_terminated',
'Import/csv_enclosed',
'Import/csv_escaped',
'Import/csv_col_names',
':group:end',
':group:' . __('CSV using LOAD DATA'),
'Import/ldi_replace',
'Import/ldi_ignore',
'Import/ldi_terminated',
'Import/ldi_enclosed',
'Import/ldi_escaped',
'Import/ldi_local_option');
$forms['Import']['Open_Document'] = array(
':group:' . __('OpenDocument Spreadsheet'),
'Import/ods_col_names',
'Import/ods_empty_rows',
'Import/ods_recognize_percentages',
'Import/ods_recognize_currency');
$forms['Export']['Export_defaults'] = array(
'Export/method',
':group:' . __('Quick'),
'Export/quick_export_onserver',
'Export/quick_export_onserver_overwrite',
':group:end',
':group:' . __('Custom'),
'Export/format',
'Export/compression',
'Export/charset',
'Export/lock_tables',
'Export/as_separate_files',
'Export/asfile' => ':group',
'Export/onserver',
'Export/onserver_overwrite',
':group:end',
'Export/file_template_table',
'Export/file_template_database',
'Export/file_template_server');
$forms['Export']['Sql'] = array(
'Export/sql_include_comments' => ':group',
'Export/sql_dates',
'Export/sql_relation',
'Export/sql_mime',
':group:end',
'Export/sql_use_transaction',
'Export/sql_disable_fk',
'Export/sql_views_as_tables',
'Export/sql_metadata',
'Export/sql_compatibility',
'Export/sql_structure_or_data',
':group:' . __('Structure'),
'Export/sql_drop_database',
'Export/sql_create_database',
'Export/sql_drop_table',
'Export/sql_create_table' => ':group',
'Export/sql_if_not_exists',
'Export/sql_auto_increment',
':group:end',
'Export/sql_create_view',
'Export/sql_procedure_function',
'Export/sql_create_trigger',
'Export/sql_backquotes',
':group:end',
':group:' . __('Data'),
'Export/sql_delayed',
'Export/sql_ignore',
'Export/sql_type',
'Export/sql_insert_syntax',
'Export/sql_max_query_size',
'Export/sql_hex_for_binary',
'Export/sql_utc_time');
$forms['Export']['CodeGen'] = array(
'Export/codegen_format');
$forms['Export']['Csv'] = array(
':group:' . __('CSV'),
'Export/csv_separator',
'Export/csv_enclosed',
'Export/csv_escaped',
'Export/csv_terminated',
'Export/csv_null',
'Export/csv_removeCRLF',
'Export/csv_columns',
':group:end',
':group:' . __('CSV for MS Excel'),
'Export/excel_null',
'Export/excel_removeCRLF',
'Export/excel_columns',
'Export/excel_edition');
$forms['Export']['Latex'] = array(
'Export/latex_caption',
'Export/latex_structure_or_data',
':group:' . __('Structure'),
'Export/latex_structure_caption',
'Export/latex_structure_continued_caption',
'Export/latex_structure_label',
'Export/latex_relation',
'Export/latex_comments',
'Export/latex_mime',
':group:end',
':group:' . __('Data'),
'Export/latex_columns',
'Export/latex_data_caption',
'Export/latex_data_continued_caption',
'Export/latex_data_label',
'Export/latex_null');
$forms['Export']['Microsoft_Office'] = array(
':group:' . __('Microsoft Word 2000'),
'Export/htmlword_structure_or_data',
'Export/htmlword_null',
'Export/htmlword_columns');
$forms['Export']['Open_Document'] = array(
':group:' . __('OpenDocument Spreadsheet'),
'Export/ods_columns',
'Export/ods_null',
':group:end',
':group:' . __('OpenDocument Text'),
'Export/odt_structure_or_data',
':group:' . __('Structure'),
'Export/odt_relation',
'Export/odt_comments',
'Export/odt_mime',
':group:end',
':group:' . __('Data'),
'Export/odt_columns',
'Export/odt_null');
$forms['Export']['Texy'] = array(
'Export/texytext_structure_or_data',
':group:' . __('Data'),
'Export/texytext_null',
'Export/texytext_columns');

View File

@ -0,0 +1,49 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\TableController
*
* @package PMA
*/
namespace PMA\libraries\controllers;
use PMA\libraries\DatabaseInterface;
use PMA\libraries\di\Container;
use PMA\libraries\Response;
require_once 'libraries/database_interface.inc.php';
/**
* Base class for all of controller
*
* @package PhpMyAdmin
*/
abstract class Controller
{
/**
* @var Response
*/
protected $response;
/**
* @var DatabaseInterface
*/
protected $dbi;
/**
* @var \PMA\libraries\di\Container
*/
protected $container;
/**
* Constructor
*/
public function __construct()
{
$container = Container::getDefaultContainer();
$this->container = $container;
$this->dbi = $this->container->get('dbi');
$this->response = $this->container->get('response');
}
}

View File

@ -0,0 +1,30 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\DatabaseController
*
* @package PMA
*/
namespace PMA\libraries\controllers;
/**
* Handles database related logic
*
* @package PhpMyAdmin
*/
abstract class DatabaseController extends Controller
{
/**
* @var string $db
*/
protected $db;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->db = $this->container->get('db');
}
}

View File

@ -0,0 +1,36 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\TableController
*
* @package PMA
*/
namespace PMA\libraries\controllers;
/**
* Handles table related logic
*
* @package PhpMyAdmin
*/
abstract class TableController extends Controller
{
/**
* @var string $db
*/
protected $db;
/**
* @var string $table
*/
protected $table;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->db = $this->container->get('db');
$this->table = $this->container->get('table');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,262 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\libraries\controllers\server\ServerBinlogController
*
* @package PMA\libraries\controllers\server
*/
namespace PMA\libraries\controllers\server;
use PMA\libraries\controllers\Controller;
use PMA\libraries\DatabaseInterface;
use PMA\libraries\Message;
use PMA\libraries\Util;
use PMA\libraries\Template;
/**
* Handles viewing binary logs
*
* @package PMA\libraries\controllers\server
*/
class ServerBinlogController extends Controller
{
/**
* array binary log files
*/
protected $binary_logs;
/**
* Constructs ServerBinlogController
*/
public function __construct()
{
parent::__construct();
$this->binary_logs = $this->dbi->fetchResult(
'SHOW MASTER LOGS',
'Log_name',
null,
null,
DatabaseInterface::QUERY_STORE
);
}
/**
* Index action
*
* @return void
*/
public function indexAction()
{
/**
* Does the common work
*/
include_once 'libraries/server_common.inc.php';
$url_params = array();
if (! isset($_REQUEST['log'])
|| ! array_key_exists($_REQUEST['log'], $this->binary_logs)
) {
$_REQUEST['log'] = '';
} else {
$url_params['log'] = $_REQUEST['log'];
}
if (!empty($_REQUEST['dontlimitchars'])) {
$url_params['dontlimitchars'] = 1;
}
$this->response->addHTML(PMA_getHtmlForSubPageHeader('binlog'));
$this->response->addHTML($this->_getLogSelector($url_params));
$this->response->addHTML($this->_getLogInfo($url_params));
}
/**
* Returns the html for log selector.
*
* @param array $url_params links parameters
*
* @return string
*/
private function _getLogSelector($url_params)
{
return Template::get('server/binlog/log_selector')->render(
array(
'url_params' => $url_params,
'binary_logs' => $this->binary_logs,
)
);
}
/**
* Returns the html for binary log information.
*
* @param array $url_params links parameters
*
* @return string
*/
private function _getLogInfo($url_params)
{
/**
* Need to find the real end of rows?
*/
if (! isset($_REQUEST['pos'])) {
$pos = 0;
} else {
/* We need this to be a integer */
$pos = (int) $_REQUEST['pos'];
}
$sql_query = 'SHOW BINLOG EVENTS';
if (! empty($_REQUEST['log'])) {
$sql_query .= ' IN \'' . $_REQUEST['log'] . '\'';
}
$sql_query .= ' LIMIT ' . $pos . ', ' . intval($GLOBALS['cfg']['MaxRows']);
/**
* Sends the query
*/
$result = $this->dbi->query($sql_query);
/**
* prepare some vars for displaying the result table
*/
// Gets the list of fields properties
if (isset($result) && $result) {
$num_rows = $this->dbi->numRows($result);
} else {
$num_rows = 0;
}
if (empty($_REQUEST['dontlimitchars'])) {
$dontlimitchars = false;
} else {
$dontlimitchars = true;
$url_params['dontlimitchars'] = 1;
}
//html output
$html = Util::getMessage(Message::success(), $sql_query);
$html .= '<table id="binlogTable">'
. '<thead>'
. '<tr>'
. '<td colspan="6" class="center">';
$html .= $this->_getNavigationRow($url_params, $pos, $num_rows, $dontlimitchars);
$html .= '</td>'
. '</tr>'
. '<tr>'
. '<th>' . __('Log name') . '</th>'
. '<th>' . __('Position') . '</th>'
. '<th>' . __('Event type') . '</th>'
. '<th>' . __('Server ID') . '</th>'
. '<th>' . __('Original position') . '</th>'
. '<th>' . __('Information') . '</th>'
. '</tr>'
. '</thead>'
. '<tbody>';
$html .= $this->_getAllLogItemInfo($result, $dontlimitchars);
$html .= '</tbody>'
. '</table>';
return $html;
}
/**
* Returns the html for Navigation Row.
*
* @param array $url_params Links parameters
* @param int $pos Position to display
* @param int $num_rows Number of results row
* @param bool $dontlimitchars Whether limit chars
*
* @return string
*/
private function _getNavigationRow($url_params, $pos, $num_rows, $dontlimitchars)
{
$html = "";
// we do not know how much rows are in the binlog
// so we can just force 'NEXT' button
if ($pos > 0) {
$this_url_params = $url_params;
if ($pos > $GLOBALS['cfg']['MaxRows']) {
$this_url_params['pos'] = $pos - $GLOBALS['cfg']['MaxRows'];
}
$html .= '<a href="server_binlog.php'
. PMA_URL_getCommon($this_url_params) . '"';
if (Util::showIcons('TableNavigationLinksMode')) {
$html .= ' title="' . _pgettext('Previous page', 'Previous') . '">';
} else {
$html .= '>' . _pgettext('Previous page', 'Previous');
} // end if... else...
$html .= ' &lt; </a> - ';
}
$this_url_params = $url_params;
if ($pos > 0) {
$this_url_params['pos'] = $pos;
}
if ($dontlimitchars) {
unset($this_url_params['dontlimitchars']);
$tempTitle = __('Truncate Shown Queries');
$tempImgMode = 'partial';
} else {
$this_url_params['dontlimitchars'] = 1;
$tempTitle = __('Show Full Queries');
$tempImgMode = 'full';
}
$html .= '<a href="server_binlog.php' . PMA_URL_getCommon($this_url_params)
. '" title="' . $tempTitle . '">'
. '<img src="' . $GLOBALS['pmaThemeImage'] . 's_' . $tempImgMode
. 'text.png" alt="' . $tempTitle . '" /></a>';
// we do not now how much rows are in the binlog
// so we can just force 'NEXT' button
if ($num_rows >= $GLOBALS['cfg']['MaxRows']) {
$this_url_params = $url_params;
$this_url_params['pos'] = $pos + $GLOBALS['cfg']['MaxRows'];
$html .= ' - <a href="server_binlog.php'
. PMA_URL_getCommon($this_url_params)
. '"';
if (Util::showIcons('TableNavigationLinksMode')) {
$html .= ' title="' . _pgettext('Next page', 'Next') . '">';
} else {
$html .= '>' . _pgettext('Next page', 'Next');
} // end if... else...
$html .= ' &gt; </a>';
}
return $html;
}
/**
* Returns the html for all binary log items.
*
* @param resource $result MySQL Query result
* @param bool $dontlimitchars Whether limit chars
*
* @return string
*/
private function _getAllLogItemInfo($result, $dontlimitchars)
{
$html = "";
$odd_row = true;
while ($value = $this->dbi->fetchAssoc($result)) {
$html .= Template::get('server/binlog/log_row')->render(
array(
'odd_row' => $odd_row,
'value' => $value,
'dontlimitchars' => $dontlimitchars,
)
);
$odd_row = !$odd_row;
}
return $html;
}
}

View File

@ -0,0 +1,74 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\libraries\controllers\server\ServerCollationsController
*
* @package PMA\libraries\controllers\server
*/
namespace PMA\libraries\controllers\server;
use PMA\libraries\controllers\Controller;
use PMA\libraries\Template;
/**
* Handles viewing character sets and collations
*
* @package PMA\libraries\controllers\server
*/
class ServerCollationsController extends Controller
{
/**
* Index action
*
* @return void
*/
public function indexAction()
{
/**
* Does the common work
*/
include_once 'libraries/server_common.inc.php';
/**
* Includes the required charset library
*/
include_once 'libraries/mysql_charsets.inc.php';
$this->response->addHTML(PMA_getHtmlForSubPageHeader('collations'));
$this->response->addHTML(
$this->_getHtmlForCharsets(
$GLOBALS['mysql_charsets'],
$GLOBALS['mysql_collations'],
$GLOBALS['mysql_charsets_descriptions'],
$GLOBALS['mysql_default_collations'],
$GLOBALS['mysql_collations_available']
)
);
}
/**
* Returns the html for server Character Sets and Collations.
*
* @param array $mysqlCharsets Mysql Charsets list
* @param array $mysqlCollations Mysql Collations list
* @param array $mysqlCharsetsDesc Charsets descriptions
* @param array $mysqlDftCollations Default Collations list
* @param array $mysqlCollAvailable Available Collations list
*
* @return string
*/
function _getHtmlForCharsets($mysqlCharsets, $mysqlCollations,
$mysqlCharsetsDesc, $mysqlDftCollations, $mysqlCollAvailable
) {
return Template::get('server/collations/charsets')->render(
array(
'mysqlCharsets' => $mysqlCharsets,
'mysqlCollations' => $mysqlCollations,
'mysqlCharsetsDesc' => $mysqlCharsetsDesc,
'mysqlDftCollations' => $mysqlDftCollations,
'mysqlCollAvailable' => $mysqlCollAvailable,
)
);
}
}

View File

@ -0,0 +1,587 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\libraries\controllers\server\ServerDatabasesController
*
* @package PMA\libraries\controllers\server
*/
namespace PMA\libraries\controllers\server;
use PMA\libraries\controllers\Controller;
use PMA\libraries\Message;
use PMA\libraries\Template;
use PMA\libraries\Util;
/**
* Handles viewing and creating and deleting databases
*
* @package PMA\libraries\controllers\server
*/
class ServerDatabasesController extends Controller
{
/**
* @var array array of database details
*/
private $_databases;
/**
* @var int number of databases
*/
private $_database_count;
/**
* @var string sort by column
*/
private $_sort_by;
/**
* @var string sort order of databases
*/
private $_sort_order;
/**
* @var boolean whether to show database statistics
*/
private $_dbstats;
/**
* @var int position in list navigation
*/
private $_pos;
/**
* Index action
*
* @return void
*/
public function indexAction()
{
include_once 'libraries/check_user_privileges.lib.php';
if (isset($_REQUEST['drop_selected_dbs'])
&& $GLOBALS['is_ajax_request']
&& ($GLOBALS['is_superuser'] || $GLOBALS['cfg']['AllowUserDropDatabase'])
) {
$this->dropDatabasesAction();
return;
}
include_once 'libraries/replication.inc.php';
include_once 'libraries/mysql_charsets.inc.php';
if (! empty($_POST['new_db'])
&& $GLOBALS['is_ajax_request']
) {
$this->createDatabaseAction();
return;
}
include_once 'libraries/server_common.inc.php';
$header = $this->response->getHeader();
$scripts = $header->getScripts();
$scripts->addFile('server_databases.js');
$this->_setSortDetails();
$this->_dbstats = empty($_REQUEST['dbstats']) ? false : true;
$this->_pos = empty($_REQUEST['pos']) ? 0 : (int) $_REQUEST['pos'];
/**
* Displays the sub-page heading
*/
$header_type = $this->_dbstats ? "database_statistics" : "databases";
$this->response->addHTML(PMA_getHtmlForSubPageHeader($header_type));
/**
* Displays For Create database.
*/
$html = '';
if ($GLOBALS['cfg']['ShowCreateDb']) {
$html .= Template::get('server/databases/create')->render();
}
/**
* Gets the databases list
*/
if ($GLOBALS['server'] > 0) {
$this->_databases = $this->dbi->getDatabasesFull(
null, $this->_dbstats, null, $this->_sort_by,
$this->_sort_order, $this->_pos, true
);
$this->_database_count = count($GLOBALS['dblist']->databases);
} else {
$this->_database_count = 0;
}
/**
* Displays the page
*/
if ($this->_database_count > 0 && ! empty($this->_databases)) {
$html .= $this->_getHtmlForDatabases($replication_types);
} else {
$html .= __('No databases');
}
$this->response->addHTML($html);
}
/**
* Handles creating a new database
*
* @return void
*/
public function createDatabaseAction()
{
/**
* Builds and executes the db creation sql query
*/
$sql_query = 'CREATE DATABASE ' . Util::backquote($_POST['new_db']);
if (! empty($_POST['db_collation'])) {
list($db_charset) = explode('_', $_POST['db_collation']);
if (in_array($db_charset, $GLOBALS['mysql_charsets'])
&& in_array($_POST['db_collation'], $GLOBALS['mysql_collations'][$db_charset])
) {
$sql_query .= ' DEFAULT'
. PMA_generateCharsetQueryPart($_POST['db_collation']);
}
}
$sql_query .= ';';
$result = $GLOBALS['dbi']->tryQuery($sql_query);
if (! $result) {
// avoid displaying the not-created db name in header or navi panel
$GLOBALS['db'] = '';
$message = Message::rawError($GLOBALS['dbi']->getError());
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $message);
} else {
$GLOBALS['db'] = $_POST['new_db'];
$message = Message::success(__('Database %1$s has been created.'));
$message->addParam($_POST['new_db']);
$this->response->addJSON('message', $message);
$this->response->addJSON(
'sql_query', Util::getMessage(null, $sql_query, 'success')
);
$url_query = PMA_URL_getCommon(array('db' => $_POST['new_db']));
$this->response->addJSON(
'url_query',
Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabDatabase'], 'database'
)
. $url_query . '&amp;db='
. urlencode($_POST['new_db'])
);
}
}
/**
* Handles dropping multiple databases
*
* @return void
*/
public function dropDatabasesAction()
{
if (! isset($_REQUEST['selected_dbs'])) {
$message = Message::error(__('No databases selected.'));
} else {
$action = 'server_databases.php';
$err_url = $action . PMA_URL_getCommon();
$GLOBALS['submit_mult'] = 'drop_db';
$GLOBALS['mult_btn'] = __('Yes');
include 'libraries/mult_submits.inc.php';
if (empty($message)) { // no error message
$number_of_databases = count($selected);
$message = Message::success(
_ngettext(
'%1$d database has been dropped successfully.',
'%1$d databases have been dropped successfully.',
$number_of_databases
)
);
$message->addParam($number_of_databases);
}
}
if ($message instanceof Message) {
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
}
}
/**
* Extracts parameters $sort_order and $sort_by
*
* @return void
*/
private function _setSortDetails()
{
if (empty($_REQUEST['sort_by'])) {
$this->_sort_by = 'SCHEMA_NAME';
} else {
$sort_by_whitelist = array(
'SCHEMA_NAME',
'DEFAULT_COLLATION_NAME',
'SCHEMA_TABLES',
'SCHEMA_TABLE_ROWS',
'SCHEMA_DATA_LENGTH',
'SCHEMA_INDEX_LENGTH',
'SCHEMA_LENGTH',
'SCHEMA_DATA_FREE'
);
if (in_array($_REQUEST['sort_by'], $sort_by_whitelist)) {
$this->_sort_by = $_REQUEST['sort_by'];
} else {
$this->_sort_by = 'SCHEMA_NAME';
}
}
if (isset($_REQUEST['sort_order'])
&& mb_strtolower($_REQUEST['sort_order']) == 'desc'
) {
$this->_sort_order = 'desc';
} else {
$this->_sort_order = 'asc';
}
}
/**
* Returns the html for Database List
*
* @param array $replication_types replication types
*
* @return string
*/
private function _getHtmlForDatabases($replication_types)
{
$html = '<div id="tableslistcontainer">';
$first_database = reset($this->_databases);
// table col order
$column_order = $this->_getColumnOrder();
$_url_params = array(
'pos' => $this->_pos,
'dbstats' => $this->_dbstats,
'sort_by' => $this->_sort_by,
'sort_order' => $this->_sort_order,
);
$html .= Util::getListNavigator(
$this->_database_count, $this->_pos, $_url_params,
'server_databases.php', 'frame_content', $GLOBALS['cfg']['MaxDbList']
);
$_url_params['pos'] = $this->_pos;
$html .= '<form class="ajax" action="server_databases.php" ';
$html .= 'method="post" name="dbStatsForm" id="dbStatsForm">' . "\n";
$html .= PMA_URL_getHiddenInputs($_url_params);
$_url_params['sort_by'] = 'SCHEMA_NAME';
$_url_params['sort_order']
= ($this->_sort_by == 'SCHEMA_NAME' && $this->_sort_order == 'asc')
? 'desc' : 'asc';
// calculate aggregate stats to display in footer
foreach ($this->_databases as $current) {
foreach ($column_order as $stat_name => $stat) {
if (array_key_exists($stat_name, $current)
&& is_numeric($stat['footer'])
) {
$column_order[$stat_name]['footer'] += $current[$stat_name];
}
}
}
// database table
$html .= '<table id="tabledatabases" class="data">' . "\n";
$html .= $this->_getHtmlForTableHeader(
$_url_params, $column_order, $first_database
);
$html .= $this->_getHtmlForTableBody($column_order, $replication_types);
$html .= $this->_getHtmlForTableFooter($column_order, $first_database);
$html .= '</table>' . "\n";
$html .= $this->_getHtmlForTableFooterButtons();
if (empty($this->_dbstats)) {
//we should put notice above database list
$html .= $this->_getHtmlForNoticeEnableStatistics();
}
$html .= '</form>';
$html .= '</div>';
return $html;
}
/**
* Prepares the $column_order array
*
* @return array
*/
private function _getColumnOrder()
{
$column_order = array();
$column_order['DEFAULT_COLLATION_NAME'] = array(
'disp_name' => __('Collation'),
'description_function' => 'PMA_getCollationDescr',
'format' => 'string',
'footer' => PMA_getServerCollation(),
);
$column_order['SCHEMA_TABLES'] = array(
'disp_name' => __('Tables'),
'format' => 'number',
'footer' => 0,
);
$column_order['SCHEMA_TABLE_ROWS'] = array(
'disp_name' => __('Rows'),
'format' => 'number',
'footer' => 0,
);
$column_order['SCHEMA_DATA_LENGTH'] = array(
'disp_name' => __('Data'),
'format' => 'byte',
'footer' => 0,
);
$column_order['SCHEMA_INDEX_LENGTH'] = array(
'disp_name' => __('Indexes'),
'format' => 'byte',
'footer' => 0,
);
$column_order['SCHEMA_LENGTH'] = array(
'disp_name' => __('Total'),
'format' => 'byte',
'footer' => 0,
);
$column_order['SCHEMA_DATA_FREE'] = array(
'disp_name' => __('Overhead'),
'format' => 'byte',
'footer' => 0,
);
return $column_order;
}
/**
* Returns the html for Table footer buttons
*
* @return string
*/
private function _getHtmlForTableFooterButtons()
{
if (! $GLOBALS['is_superuser']
&& ! $GLOBALS['cfg']['AllowUserDropDatabase']
) {
return '';
}
$html = Util::getWithSelected(
$GLOBALS['pmaThemeImage'], $GLOBALS['text_dir'], "dbStatsForm"
);
$html .= Util::getButtonOrImage(
'',
'mult_submit' . ' ajax',
'drop_selected_dbs',
__('Drop'), 'b_deltbl.png'
);
return $html;
}
/**
* Returns the html for Table footer
*
* @param string $column_order column order
* @param string $first_database first database
*
* @return string
*/
private function _getHtmlForTableFooter($column_order, $first_database)
{
return Template::get('server/databases/table_footer')->render(
array(
'column_order' => $column_order,
'first_database' => $first_database,
'master_replication' => $GLOBALS['replication_info']['master']['status'],
'slave_replication' => $GLOBALS['replication_info']['slave']['status'],
'databaseCount' => $this->_database_count,
)
);
}
/**
* Returns the html for Database List
*
* @param array $column_order column order
* @param array $replication_types replication types
*
* @return string
*/
private function _getHtmlForTableBody($column_order, $replication_types)
{
$odd_row = true;
$html = '<tbody>' . "\n";
foreach ($this->_databases as $current) {
$tr_class = $odd_row ? 'odd' : 'even';
if ($this->dbi->isSystemSchema($current['SCHEMA_NAME'], true)) {
$tr_class .= ' noclick';
}
$odd_row = ! $odd_row;
$generated_html = $this->_buildHtmlForDb(
$current,
$GLOBALS['url_query'],
$column_order,
$replication_types,
$GLOBALS['replication_info'],
$tr_class
);
$html .= $generated_html;
} // end foreach ($this->_databases as $key => $current)
$html .= '</tbody>';
return $html;
}
/**
* Builds the HTML for one database to display in the list
* of databases from server_databases.php
*
* @param array $current current database
* @param string $url_query url query
* @param array $column_order column order
* @param array $replication_types replication types
* @param array $replication_info replication info
* @param string $tr_class HTMl class for the row
*
* @return array $column_order, $out
*/
function _buildHtmlForDb(
$current, $url_query, $column_order,
$replication_types, $replication_info, $tr_class = ''
) {
$master_replication = $slave_replication = '';
foreach ($replication_types as $type) {
if ($replication_info[$type]['status']) {
$out = '';
$key = array_search(
$current["SCHEMA_NAME"],
$replication_info[$type]['Ignore_DB']
);
if (mb_strlen($key) > 0) {
$out = Util::getIcon(
's_cancel.png',
__('Not replicated')
);
} else {
$key = array_search(
$current["SCHEMA_NAME"], $replication_info[$type]['Do_DB']
);
if (mb_strlen($key) > 0
|| count($replication_info[$type]['Do_DB']) == 0
) {
// if ($key != null) did not work for index "0"
$out = Util::getIcon(
's_success.png',
__('Replicated')
);
}
}
if ($type == 'master') {
$master_replication = $out;
} elseif ($type == 'slave') {
$slave_replication = $out;
}
}
}
return Template::get('server/databases/table_row')->render(
array(
'current' => $current,
'tr_class' => $tr_class,
'url_query' => $url_query,
'column_order' => $column_order,
'master_replication_status'
=> $GLOBALS['replication_info']['master']['status'],
'master_replication' => $master_replication,
'slave_replication_status'
=> $GLOBALS['replication_info']['slave']['status'],
'slave_replication' => $slave_replication,
)
);
}
/**
* Returns the html for table header
*
* @param array $_url_params url params
* @param array $column_order column order
* @param array $first_database database to show
*
* @return string
*/
private function _getHtmlForTableHeader(
$_url_params, $column_order, $first_database
) {
return Template::get('server/databases/table_header')->render(
array(
'_url_params' => $_url_params,
'sort_by' => $this->_sort_by,
'sort_order' => $this->_sort_order,
'sort_order_text' => ($this->_sort_order == 'asc'
? __('Ascending') : __('Descending')),
'column_order' => $column_order,
'first_database' => $first_database,
'master_replication'
=> $GLOBALS['replication_info']['master']['status'],
'slave_replication'
=> $GLOBALS['replication_info']['slave']['status'],
)
);
}
/**
* Returns the html for Enable Statistics
*
* @return string
*/
private function _getHtmlForNoticeEnableStatistics()
{
$html = '';
$notice = Message::notice(
__(
'Note: Enabling the database statistics here might cause '
. 'heavy traffic between the web server and the MySQL server.'
)
)->getDisplay();
$html .= $notice;
$items = array();
$items[] = array(
'content' => '<strong>' . "\n"
. __('Enable statistics')
. '</strong><br />' . "\n",
'class' => 'li_switch_dbstats',
'url' => array(
'href' => 'server_databases.php'
. $GLOBALS['url_query'] . '&amp;dbstats=1',
'title' => __('Enable statistics')
),
);
$html .= Template::get('list/unordered')->render(
array('items' => $items,)
);
return $html;
}
}

View File

@ -0,0 +1,93 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\libraries\controllers\server\ServerEnginesController
*
* @package PMA\libraries\controllers\server
*/
namespace PMA\libraries\controllers\server;
use PMA\libraries\controllers\Controller;
use PMA\libraries\StorageEngine;
use PMA\libraries\Template;
use PMA\libraries\Util;
/**
* Handles viewing storage engine details
*
* @package PMA\libraries\controllers\server
*/
class ServerEnginesController extends Controller
{
/**
* Index action
*
* @return void
*/
public function indexAction()
{
/**
* Does the common work
*/
require 'libraries/server_common.inc.php';
/**
* Displays the sub-page heading
*/
$this->response->addHTML(PMA_getHtmlForSubPageHeader('engines'));
/**
* Did the user request information about a certain storage engine?
*/
if (empty($_REQUEST['engine'])
|| ! StorageEngine::isValid($_REQUEST['engine'])
) {
$this->response->addHTML($this->_getHtmlForAllServerEngines());
} else {
$engine = StorageEngine::getEngine($_REQUEST['engine']);
$this->response->addHTML($this->_getHtmlForServerEngine($engine));
}
}
/**
* Return HTML with all Storage Engine information
*
* @return string
*/
private function _getHtmlForAllServerEngines()
{
return Template::get('server/engines/engines')->render(
array('engines' => StorageEngine::getStorageEngines())
);
}
/**
* Return HTML for a given Storage Engine
*
* @param StorageEngine $engine storage engine
*
* @return string
*/
private function _getHtmlForServerEngine($engine)
{
$pageOutput = ! empty($_REQUEST['page'])
? $engine->getPage($_REQUEST['page']) : '';
/**
* Displays details about a given Storage Engine
*/
return Template::get('server/engines/engine')->render(
array(
'title' => $engine->getTitle(),
'helpPage' => $engine->getMysqlHelpPage(),
'comment' => $engine->getComment(),
'infoPages' => $engine->getInfoPages(),
'support' => $engine->getSupportInformationMessage(),
'variables' => $engine->getHtmlVariables(),
'pageOutput' => $pageOutput,
)
);
}
}

View File

@ -0,0 +1,106 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\libraries\controllers\server\ServerPluginsController
*
* @package PMA\libraries\controllers\server
*/
namespace PMA\libraries\controllers\server;
use PMA\libraries\controllers\Controller;
use PMA\libraries\Template;
/**
* Handles viewing server plugin details
*
* @package PMA\libraries\controllers\server
*/
class ServerPluginsController extends Controller
{
/**
* @var array plugin details
*/
protected $plugins;
/**
* Constructs ServerPluginsController
*/
public function __construct()
{
parent::__construct();
$this->_setServerPlugins();
}
/**
* Index action
*
* @return void
*/
public function indexAction()
{
include 'libraries/server_common.inc.php';
$header = $this->response->getHeader();
$scripts = $header->getScripts();
$scripts->addFile('jquery/jquery.tablesorter.js');
$scripts->addFile('server_plugins.js');
/**
* Displays the page
*/
$this->response->addHTML(PMA_getHtmlForSubPageHeader('plugins'));
$this->response->addHTML($this->_getPluginsHtml());
}
/**
* Sets details about server plugins
*
* @return void
*/
private function _setServerPlugins()
{
$sql = "SELECT plugin_name,
plugin_type,
(plugin_status = 'ACTIVE') AS is_active,
plugin_type_version,
plugin_author,
plugin_description,
plugin_license
FROM information_schema.plugins
ORDER BY plugin_type, plugin_name";
$res = $this->dbi->query($sql);
$this->plugins = array();
while ($row = $this->dbi->fetchAssoc($res)) {
$this->plugins[$row['plugin_type']][] = $row;
}
$this->dbi->freeResult($res);
ksort($this->plugins);
}
/**
* Returns the html for plugin Tab.
*
* @return string
*/
private function _getPluginsHtml()
{
$html = '<div id="plugins_plugins">';
$html .= Template::get('server/plugins/section_links')
->render(array('plugins' => $this->plugins));
foreach ($this->plugins as $plugin_type => $plugin_list) {
$html .= Template::get('server/plugins/section')
->render(
array(
'plugin_type' => $plugin_type,
'plugin_list' => $plugin_list,
)
);
}
$html .= '</div>';
return $html;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\TableChartController
*
* @package PMA
*/
namespace PMA\libraries\controllers\table;
use PMA\libraries\controllers\TableController;
use PMA\libraries\Message;
use PMA\libraries\Template;
use PMA\libraries\Util;
/**
* Handles table related logic
*
* @package PhpMyAdmin
*/
class TableChartController extends TableController
{
/**
* @var string $sql_query
*/
protected $sql_query;
/**
* @var string $url_query
*/
protected $url_query;
/**
* @var array $cfg
*/
protected $cfg;
/**
* Constructor
*
* @param string $sql_query Query
* @param string $url_query Query URL
* @param array $cfg Configuration
*/
public function __construct($sql_query, $url_query, $cfg)
{
parent::__construct();
$this->sql_query = $sql_query;
$this->url_query = $url_query;
$this->cfg = $cfg;
}
/**
* Execute the query and return the result
*
* @return void
*/
public function indexAction()
{
if (isset($_REQUEST['ajax_request'])
&& isset($_REQUEST['pos'])
&& isset($_REQUEST['session_max_rows'])
) {
$this->ajaxAction();
return;
}
// Throw error if no sql query is set
if (!isset($this->sql_query) || $this->sql_query == '') {
$this->response->setRequestStatus(false);
$this->response->addHTML(
Message::error(__('No SQL query was set to fetch data.'))
);
return;
}
$this->response->getHeader()->getScripts()->addFiles(
array(
'chart.js',
'tbl_chart.js',
'jqplot/jquery.jqplot.js',
'jqplot/plugins/jqplot.barRenderer.js',
'jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
'jqplot/plugins/jqplot.canvasTextRenderer.js',
'jqplot/plugins/jqplot.categoryAxisRenderer.js',
'jqplot/plugins/jqplot.dateAxisRenderer.js',
'jqplot/plugins/jqplot.pointLabels.js',
'jqplot/plugins/jqplot.pieRenderer.js',
'jqplot/plugins/jqplot.highlighter.js'
)
);
/**
* Extract values for common work
* @todo Extract common files
*/
$db = &$this->db;
$table = &$this->table;
$url_params = array();
/**
* Runs common work
*/
if (mb_strlen($this->table)) {
$url_params['goto'] = Util::getScriptNameForOption(
$this->cfg['DefaultTabTable'], 'table'
);
$url_params['back'] = 'tbl_sql.php';
include 'libraries/tbl_common.inc.php';
include 'libraries/tbl_info.inc.php';
} elseif (mb_strlen($this->db)) {
$url_params['goto'] = Util::getScriptNameForOption(
$this->cfg['DefaultTabDatabase'], 'database'
);
$url_params['back'] = 'sql.php';
include 'libraries/db_common.inc.php';
} else {
$url_params['goto'] = Util::getScriptNameForOption(
$this->cfg['DefaultTabServer'], 'server'
);
$url_params['back'] = 'sql.php';
include 'libraries/server_common.inc.php';
}
$data = array();
$result = $this->dbi->tryQuery($this->sql_query);
$fields_meta = $this->dbi->getFieldsMeta($result);
while ($row = $this->dbi->fetchAssoc($result)) {
$data[] = $row;
}
$keys = array_keys($data[0]);
$numeric_types = array('int', 'real');
$numeric_column_count = 0;
foreach ($keys as $idx => $key) {
if (in_array($fields_meta[$idx]->type, $numeric_types)) {
$numeric_column_count++;
}
}
if ($numeric_column_count == 0) {
$this->response->setRequestStatus(false);
$this->response->addJSON(
'message',
__('No numeric columns present in the table to plot.')
);
return;
}
$url_params['db'] = $this->db;
$url_params['reload'] = 1;
/**
* Displays the page
*/
$this->response->addHTML(
Template::get('table/chart/tbl_chart')->render(
array(
'url_query' => $this->url_query,
'url_params' => $url_params,
'keys' => $keys,
'fields_meta' => $fields_meta,
'numeric_types' => $numeric_types,
'numeric_column_count' => $numeric_column_count,
'sql_query' => $this->sql_query
)
)
);
}
/**
* Handle ajax request
*
* @return void
*/
public function ajaxAction()
{
/**
* Extract values for common work
* @todo Extract common files
*/
$db = &$this->db;
$table = &$this->table;
$tableLength = mb_strlen($this->table);
$dbLength = mb_strlen($this->db);
if ($tableLength && $dbLength) {
include './libraries/tbl_common.inc.php';
}
$sql_with_limit = sprintf(
'SELECT * FROM(%s) AS `temp_res` LIMIT %s, %s',
$this->sql_query,
$_REQUEST['pos'],
$_REQUEST['session_max_rows']
);
$data = array();
$result = $this->dbi->tryQuery($sql_with_limit);
while ($row = $this->dbi->fetchAssoc($result)) {
$data[] = $row;
}
if (empty($data)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No data to display'));
return;
}
$sanitized_data = array();
foreach ($data as $data_row_number => $data_row) {
$tmp_row = array();
foreach ($data_row as $data_column => $data_value) {
$tmp_row[htmlspecialchars($data_column)] = htmlspecialchars(
$data_value
);
}
$sanitized_data[] = $tmp_row;
}
$this->response->setRequestStatus(true);
$this->response->addJSON('message', null);
$this->response->addJSON('chartData', json_encode($sanitized_data));
}
}

View File

@ -0,0 +1,213 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\TableIndexesController
*
* @package PMA
*/
namespace PMA\libraries\controllers\table;
use PMA\libraries\controllers\TableController;
use PMA\libraries\Message;
use PMA\libraries\Template;
use PMA\libraries\gis\GISVisualization;
require_once 'libraries/common.inc.php';
require_once 'libraries/db_common.inc.php';
/**
* Class TableGisVisualizationController
*
* @package PMA\libraries\controllers\table
*/
class TableGisVisualizationController extends TableController
{
/**
* @var array $url_params
*/
protected $url_params;
/**
* @var string $sql_query
*/
protected $sql_query;
/**
* @var array $visualizationSettings
*/
protected $visualizationSettings;
/**
* @var \PMA\libraries\gis\GISVisualization $visualization
*/
protected $visualization;
/**
* Constructor
*
* @param string $sql_query SQL query for retrieving GIS data
* @param array $url_params array of URL parameters
* @param string $goto goto script
* @param string $back back script
* @param array $visualizationSettings visualization settings
*/
public function __construct(
$sql_query,
$url_params,
$goto,
$back,
$visualizationSettings
) {
parent::__construct();
$this->sql_query = $sql_query;
$this->url_params = $url_params;
$this->url_params['goto'] = $goto;
$this->url_params['back'] = $back;
$this->visualizationSettings = $visualizationSettings;
}
/**
* Save to file
*
* @return void
*/
public function saveToFileAction()
{
$this->response->disable();
$file_name = $this->visualizationSettings['spatialColumn'];
$save_format = $_REQUEST['fileFormat'];
$this->visualization->toFile($file_name, $save_format);
}
/**
* Index
*
* @return void
*/
public function indexAction()
{
// Throw error if no sql query is set
if (! isset($this->sql_query) || $this->sql_query == '') {
$this->response->setRequestStatus(false);
$this->response->addHTML(
Message::error(__('No SQL query was set to fetch data.'))
);
return;
}
// Execute the query and return the result
$result = $this->dbi->tryQuery($this->sql_query);
// Get the meta data of results
$meta = $this->dbi->getFieldsMeta($result);
// Find the candidate fields for label column and spatial column
$labelCandidates = array();
$spatialCandidates = array();
foreach ($meta as $column_meta) {
if ($column_meta->type == 'geometry') {
$spatialCandidates[] = $column_meta->name;
} else {
$labelCandidates[] = $column_meta->name;
}
}
// Get settings if any posted
if (PMA_isValid($_REQUEST['visualizationSettings'], 'array')) {
$this->visualizationSettings = $_REQUEST['visualizationSettings'];
}
if (!isset($this->visualizationSettings['labelColumn'])
&& isset($labelCandidates[0])
) {
$this->visualizationSettings['labelColumn'] = '';
}
// If spatial column is not set, use first geometric column as spatial column
if (! isset($this->visualizationSettings['spatialColumn'])) {
$this->visualizationSettings['spatialColumn'] = $spatialCandidates[0];
}
// Convert geometric columns from bytes to text.
$pos = isset($_REQUEST['pos']) ? $_REQUEST['pos']
: $_SESSION['tmpval']['pos'];
if (isset($_REQUEST['session_max_rows'])) {
$rows = $_REQUEST['session_max_rows'];
} else {
if ($_SESSION['tmpval']['max_rows'] != 'all') {
$rows = $_SESSION['tmpval']['max_rows'];
} else {
$rows = $GLOBALS['cfg']['MaxRows'];
}
}
$this->visualization = GISVisualization::get(
$this->sql_query,
$this->visualizationSettings,
$rows,
$pos
);
if (isset($_REQUEST['saveToFile'])) {
$this->saveToFileAction();
return;
}
$this->response->getHeader()->getScripts()->addFiles(
array(
'openlayers/OpenLayers.js',
'jquery/jquery.svg.js',
'tbl_gis_visualization.js',
)
);
// If all the rows contain SRID, use OpenStreetMaps on the initial loading.
if (! isset($_REQUEST['displayVisualization'])) {
if ($this->visualization->hasSrid()) {
$this->visualizationSettings['choice'] = 'useBaseLayer';
} else {
unset($this->visualizationSettings['choice']);
}
}
$this->visualization->setUserSpecifiedSettings($this->visualizationSettings);
if ($this->visualizationSettings != null) {
foreach ($this->visualization->getSettings() as $setting => $val) {
if (! isset($this->visualizationSettings[$setting])) {
$this->visualizationSettings[$setting] = $val;
}
}
}
/**
* Displays the page
*/
$this->url_params['sql_query'] = $this->sql_query;
$downloadUrl = 'tbl_gis_visualization.php' . PMA_URL_getCommon(
array_merge(
$this->url_params,
array(
'saveToFile' => true,
'session_max_rows' => $rows,
'pos' => $pos
)
)
);
$html = Template::get('table/gis_visualization/gis_visualization')->render(
array(
'url_params' => $this->url_params,
'downloadUrl' => $downloadUrl,
'labelCandidates' => $labelCandidates,
'spatialCandidates' => $spatialCandidates,
'visualizationSettings' => $this->visualizationSettings,
'sql_query' => $this->sql_query,
'visualization' => $this->visualization->toImage('svg'),
'drawOl' => $this->visualization->asOl()
)
);
$this->response->addHTML($html);
}
}

View File

@ -0,0 +1,173 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\TableIndexesController
*
* @package PMA
*/
namespace PMA\libraries\controllers\table;
use PMA\libraries\controllers\TableController;
use PMA\libraries\Index;
use PMA\libraries\Message;
use PMA\libraries\Template;
use PMA\libraries\Util;
/**
* Class TableIndexesController
*
* @package PMA\libraries\controllers\table
*/
class TableIndexesController extends TableController
{
/**
* @var Index $index
*/
protected $index;
/**
* Constructor
*
* @param Index $index Index
*/
public function __construct($index)
{
parent::__construct();
$this->index = $index;
}
/**
* Index
*
* @return void
*/
public function indexAction()
{
if (isset($_REQUEST['do_save_data'])) {
$this->doSaveDataAction();
return;
} // end builds the new index
$this->displayFormAction();
}
/**
* Display the form to edit/create an index
*
* @return void
*/
public function displayFormAction()
{
include_once 'libraries/tbl_info.inc.php';
$add_fields = 0;
if (isset($_REQUEST['index']) && is_array($_REQUEST['index'])) {
// coming already from form
if (isset($_REQUEST['index']['columns']['names'])) {
$add_fields = count($_REQUEST['index']['columns']['names'])
- $this->index->getColumnCount();
}
if (isset($_REQUEST['add_fields'])) {
$add_fields += $_REQUEST['added_fields'];
}
} elseif (isset($_REQUEST['create_index'])) {
$add_fields = $_REQUEST['added_fields'];
} // end preparing form values
// Get fields and stores their name/type
if (isset($_REQUEST['create_edit_table'])) {
$fields = json_decode($_REQUEST['columns'], true);
$index_params = array(
'Non_unique' => ($_REQUEST['index']['Index_choice'] == 'UNIQUE')
? '0' : '1',
);
$this->index->set($index_params);
$add_fields = count($fields);
} else {
$fields = $this->dbi->getTable($this->db, $this->table)
->getNameAndTypeOfTheColumns();
}
$form_params = array(
'db' => $this->db,
'table' => $this->table,
);
if (isset($_REQUEST['create_index'])) {
$form_params['create_index'] = 1;
} elseif (isset($_REQUEST['old_index'])) {
$form_params['old_index'] = $_REQUEST['old_index'];
} elseif (isset($_REQUEST['index'])) {
$form_params['old_index'] = $_REQUEST['index'];
}
$this->response->getHeader()->getScripts()->addFile('indexes.js');
$this->response->addHTML(
Template::get('table/index_form')->render(
array(
'fields' => $fields,
'index' => $this->index,
'form_params' => $form_params,
'add_fields' => $add_fields
)
)
);
}
/**
* Process the data from the edit/create index form,
* run the query to build the new index
* and moves back to "tbl_sql.php"
*
* @return void
*/
public function doSaveDataAction()
{
$error = false;
$sql_query = $this->dbi->getTable($this->db, $this->table)
->getSqlQueryForIndexCreateOrEdit($this->index, $error);
// If there is a request for SQL previewing.
if (isset($_REQUEST['preview_sql'])) {
$this->response->addJSON(
'sql_data',
Template::get('preview_sql')
->render(
array(
'query_data' => $sql_query
)
)
);
} elseif (!$error) {
$this->dbi->query($sql_query);
if ($GLOBALS['is_ajax_request'] == true) {
$message = Message::success(
__('Table %1$s has been altered successfully.')
);
$message->addParam($this->table);
$this->response->addJSON(
'message', Util::getMessage($message, $sql_query, 'success')
);
$this->response->addJSON(
'index_table',
Index::getHtmlForIndexes(
$this->table, $this->db
)
);
} else {
include 'tbl_structure.php';
}
} else {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $error);
}
}
}

View File

@ -0,0 +1,362 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PMA\TableRelationController
*
* @package PMA
*/
namespace PMA\libraries\controllers\table;
require_once 'libraries/index.lib.php';
use PMA\libraries\controllers\TableController;
use PMA\libraries\DatabaseInterface;
use PMA\libraries\Index;
use PMA\libraries\Table;
use PMA\libraries\Template;
use PMA\libraries\Util;
/**
* Handles table relation logic
*
* @package PhpMyAdmin
*/
class TableRelationController extends TableController
{
/**
* @var array $options_array
*/
protected $options_array;
/**
* @var array $cfgRelation
*/
protected $cfgRelation;
/**
* @var array $existrel
*/
protected $existrel;
/**
* @var string $disp
*/
protected $disp;
/**
* @var string $tbl_storage_engine
*/
protected $tbl_storage_engine;
/**
* @var array $existrel_foreign
*/
protected $existrel_foreign;
/**
* @var Table $udp_query
*/
protected $upd_query;
/**
* Constructor
*
* @param array $options_array Options
* @param array $cfgRelation Config relation
* @param string $tbl_storage_engine Table storage engine
* @param array $existrel Relations
* @param array $existrel_foreign External relations
* @param string $disp Display
* @param string $upd_query Update query
*/
public function __construct($options_array, $cfgRelation, $tbl_storage_engine,
$existrel, $existrel_foreign, $disp, $upd_query
) {
parent::__construct();
$this->options_array = $options_array;
$this->cfgRelation = $cfgRelation;
$this->tbl_storage_engine = $tbl_storage_engine;
$this->existrel = $existrel;
$this->existrel_foreign = $existrel_foreign;
$this->disp = $disp;
$this->upd_query = $upd_query;
}
/**
* Index
*
* @return void
*/
public function indexAction()
{
// Send table of column names to populate corresponding dropdowns depending
// on the current selection
if (isset($_REQUEST['getDropdownValues'])
&& $_REQUEST['getDropdownValues'] === 'true'
) {
// if both db and table are selected
if (isset($_REQUEST['foreignTable'])) {
$this->getDropdownValueForTableAction();
} else { // if only the db is selected
$this->getDropdownValueForDbAction();
}
return;
}
$this->response->getHeader()->getScripts()->addFiles(
array(
'tbl_relation.js',
'indexes.js'
)
);
// Gets tables information
include_once 'libraries/tbl_info.inc.php';
// updates for Internal relations
if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
$this->updateForInternalRelationAction();
}
// updates for foreign keys
if (isset($_POST['destination_foreign_db'])) {
$this->updateForForeignKeysAction();
}
// Updates for display field
if ($this->cfgRelation['displaywork'] && isset($_POST['display_field'])) {
$this->updateForDisplayField();
}
// If we did an update, refresh our data
if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
$this->existrel = PMA_getForeigners(
$this->db, $this->table, '', 'internal'
);
}
if (isset($_POST['destination_foreign_db'])
&& Util::isForeignKeySupported($this->tbl_storage_engine)
) {
$this->existrel_foreign = PMA_getForeigners(
$this->db, $this->table, '', 'foreign'
);
}
if ($this->cfgRelation['displaywork']) {
$this->disp = PMA_getDisplayField($this->db, $this->table);
}
// display secondary level tabs if necessary
$engine = $this->dbi->getTable($this->db, $this->table)
->getStatusInfo('ENGINE');
$this->response->addHTML(
Template::get('table/secondary_tabs')->render(
array(
'url_params' => array(
'db' => $GLOBALS['db'],
'table' => $GLOBALS['table']
),
'engine' => $engine
)
)
);
$this->response->addHTML('<div id="structure_content">');
/**
* Dialog
*/
// Now find out the columns of our $table
// need to use DatabaseInterface::QUERY_STORE with $this->dbi->numRows()
// in mysqli
$columns = $this->dbi->getColumns($this->db, $this->table);
// common form
$this->response->addHTML(
Template::get('table/relation/common_form')->render(
array(
'db' => $this->db,
'table' => $this->table,
'columns' => $columns,
'cfgRelation' => $this->cfgRelation,
'tbl_storage_engine' => $this->tbl_storage_engine,
'existrel' => isset($this->existrel) ? $this->existrel : array(),
'existrel_foreign' => isset($this->existrel_foreign)
? $this->existrel_foreign['foreign_keys_data'] : array(),
'options_array' => $this->options_array
)
)
);
if (Util::isForeignKeySupported($this->tbl_storage_engine)) {
$this->response->addHTML(PMA_getHtmlForDisplayIndexes());
}
$this->response->addHTML('</div>');
}
/**
* Update for display field
*
* @return void
*/
public function updateForDisplayField()
{
if ($this->upd_query->updateDisplayField(
$this->disp, $_POST['display_field'], $this->cfgRelation
)
) {
$this->response->addHTML(
Util::getMessage(
__('Display column was successfully updated.'),
'', 'success'
)
);
}
}
/**
* Update for FK
*
* @return void
*/
public function updateForForeignKeysAction()
{
$multi_edit_columns_name = isset($_REQUEST['foreign_key_fields_name'])
? $_REQUEST['foreign_key_fields_name']
: null;
// (for now, one index name only; we keep the definitions if the
// foreign db is not the same)
list($html, $preview_sql_data, $display_query, $seen_error)
= $this->upd_query->updateForeignKeys(
$_POST['destination_foreign_db'],
$multi_edit_columns_name, $_POST['destination_foreign_table'],
$_POST['destination_foreign_column'], $this->options_array,
$this->table,
isset($this->existrel_foreign)
? $this->existrel_foreign['foreign_keys_data']
: null
);
$this->response->addHTML($html);
// If there is a request for SQL previewing.
if (isset($_REQUEST['preview_sql'])) {
PMA_previewSQL($preview_sql_data);
}
if (!empty($display_query) && !$seen_error) {
$GLOBALS['display_query'] = $display_query;
$this->response->addHTML(
Util::getMessage(
__('Your SQL query has been executed successfully.'),
null, 'success'
)
);
}
}
/**
* Update for internal relation
*
* @return void
*/
public function updateForInternalRelationAction()
{
$multi_edit_columns_name = isset($_REQUEST['fields_name'])
? $_REQUEST['fields_name']
: null;
if ($this->upd_query->updateInternalRelations(
$multi_edit_columns_name,
$_POST['destination_db'],
$_POST['destination_table'],
$_POST['destination_column'],
$this->cfgRelation,
isset($this->existrel) ? $this->existrel : null
)
) {
$this->response->addHTML(
Util::getMessage(
__('Internal relations were successfully updated.'),
'', 'success'
)
);
}
}
/**
* Send table columns for foreign table dropdown
*
* @return void
*
*/
public function getDropdownValueForTableAction()
{
$foreignTable = $_REQUEST['foreignTable'];
$table_obj = $this->dbi->getTable($_REQUEST['foreignDb'], $foreignTable);
// Since views do not have keys defined on them provide the full list of
// columns
if ($table_obj->isView()) {
$columnList = $table_obj->getColumns(false, false);
} else {
$columnList = $table_obj->getIndexedColumns(false, false);
}
$columns = array();
foreach ($columnList as $column) {
$columns[] = htmlspecialchars($column);
}
$this->response->addJSON('columns', $columns);
// @todo should be: $server->db($db)->table($table)->primary()
$primary = Index::getPrimary($foreignTable, $_REQUEST['foreignDb']);
if (false === $primary) {
return;
}
$this->response->addJSON('primary', array_keys($primary->getColumns()));
}
/**
* Send database selection values for dropdown
*
* @return void
*
*/
public function getDropdownValueForDbAction()
{
$tables = array();
$foreign = isset($_REQUEST['foreign']) && $_REQUEST['foreign'] === 'true';
if ($foreign) {
$query = 'SHOW TABLE STATUS FROM '
. Util::backquote($_REQUEST['foreignDb']);
$tables_rs = $this->dbi->query(
$query,
null,
DatabaseInterface::QUERY_STORE
);
while ($row = $this->dbi->fetchArray($tables_rs)) {
if (isset($row['Engine'])
&& mb_strtoupper($row['Engine']) == $this->tbl_storage_engine
) {
$tables[] = htmlspecialchars($row['Name']);
}
}
} else {
$query = 'SHOW TABLES FROM '
. Util::backquote($_REQUEST['foreignDb']);
$tables_rs = $this->dbi->query(
$query,
null,
DatabaseInterface::QUERY_STORE
);
while ($row = $this->dbi->fetchArray($tables_rs)) {
$tables[] = htmlspecialchars($row[0]);
}
}
$this->response->addJSON('tables', $tables);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1144
#pma/libraries/core.lib.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,494 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* set of functions for tbl_create.php and tbl_addfield.php
*
* @package PhpMyAdmin
*/
use PMA\libraries\Table;
/**
* Transforms the radio button field_key into 4 arrays
*
* @return array An array of arrays which represents column keys for each index type
*/
function PMA_getIndexedColumns()
{
$field_cnt = count($_REQUEST['field_name']);
$field_primary = json_decode($_REQUEST['primary_indexes'], true);
$field_index = json_decode($_REQUEST['indexes'], true);
$field_unique = json_decode($_REQUEST['unique_indexes'], true);
$field_fulltext = json_decode($_REQUEST['fulltext_indexes'], true);
$field_spatial = json_decode($_REQUEST['spatial_indexes'], true);
return array(
$field_cnt, $field_primary, $field_index, $field_unique,
$field_fulltext, $field_spatial
);
}
/**
* Initiate the column creation statement according to the table creation or
* add columns to a existing table
*
* @param int $field_cnt number of columns
* @param boolean $is_create_tbl true if requirement is to get the statement
* for table creation
*
* @return array $definitions An array of initial sql statements
* according to the request
*/
function PMA_buildColumnCreationStatement(
$field_cnt, $is_create_tbl = true
) {
$definitions = array();
for ($i = 0; $i < $field_cnt; ++$i) {
// '0' is also empty for php :-(
if (empty($_REQUEST['field_name'][$i])
&& $_REQUEST['field_name'][$i] != '0'
) {
continue;
}
$definition = PMA_getStatementPrefix($is_create_tbl) .
Table::generateFieldSpec(
trim($_REQUEST['field_name'][$i]),
$_REQUEST['field_type'][$i],
$_REQUEST['field_length'][$i],
$_REQUEST['field_attribute'][$i],
isset($_REQUEST['field_collation'][$i])
? $_REQUEST['field_collation'][$i]
: '',
isset($_REQUEST['field_null'][$i])
? $_REQUEST['field_null'][$i]
: 'NOT NULL',
$_REQUEST['field_default_type'][$i],
$_REQUEST['field_default_value'][$i],
isset($_REQUEST['field_extra'][$i])
? $_REQUEST['field_extra'][$i]
: false,
isset($_REQUEST['field_comments'][$i])
? $_REQUEST['field_comments'][$i]
: '',
isset($_REQUEST['field_virtuality'][$i])
? $_REQUEST['field_virtuality'][$i]
: '',
isset($_REQUEST['field_expression'][$i])
? $_REQUEST['field_expression'][$i]
: ''
);
$definition .= PMA_setColumnCreationStatementSuffix($i, $is_create_tbl);
$definitions[] = $definition;
} // end for
return $definitions;
}
/**
* Set column creation suffix according to requested position of the new column
*
* @param int $current_field_num current column number
* @param boolean $is_create_tbl true if requirement is to get the statement
* for table creation
*
* @return string $sql_suffix suffix
*/
function PMA_setColumnCreationStatementSuffix($current_field_num,
$is_create_tbl = true
) {
// no suffix is needed if request is a table creation
$sql_suffix = ' ';
if ($is_create_tbl) {
return $sql_suffix;
}
if ((string) $_REQUEST['field_where'] === 'last') {
return $sql_suffix;
}
// Only the first field can be added somewhere other than at the end
if ((int) $current_field_num === 0) {
if ((string) $_REQUEST['field_where'] === 'first') {
$sql_suffix .= ' FIRST';
} else {
$sql_suffix .= ' AFTER '
. PMA\libraries\Util::backquote($_REQUEST['after_field']);
}
} else {
$sql_suffix .= ' AFTER '
. PMA\libraries\Util::backquote(
$_REQUEST['field_name'][$current_field_num - 1]
);
}
return $sql_suffix;
}
/**
* Create relevant index statements
*
* @param array $index an array of index columns
* @param string $index_choice index choice that which represents
* the index type of $indexed_fields
* @param boolean $is_create_tbl true if requirement is to get the statement
* for table creation
*
* @return array an array of sql statements for indexes
*/
function PMA_buildIndexStatements($index, $index_choice,
$is_create_tbl = true
) {
$statement = array();
if (!count($index)) {
return $statement;
}
$sql_query = PMA_getStatementPrefix($is_create_tbl)
. ' ' . $index_choice;
if (! empty($index['Key_name']) && $index['Key_name'] != 'PRIMARY') {
$sql_query .= ' ' . PMA\libraries\Util::backquote($index['Key_name']);
}
$index_fields = array();
foreach ($index['columns'] as $key => $column) {
$index_fields[$key] = PMA\libraries\Util::backquote(
$_REQUEST['field_name'][$column['col_index']]
);
if ($column['size']) {
$index_fields[$key] .= '(' . $column['size'] . ')';
}
} // end while
$sql_query .= ' (' . implode(', ', $index_fields) . ')';
$keyBlockSizes = $index['Key_block_size'];
if (! empty($keyBlockSizes)) {
$sql_query .= " KEY_BLOCK_SIZE = "
. $GLOBALS['dbi']->escapeString($keyBlockSizes);
}
// specifying index type is allowed only for primary, unique and index only
$type = $index['Index_type'];
if ($index['Index_choice'] != 'SPATIAL'
&& $index['Index_choice'] != 'FULLTEXT'
&& in_array($type, PMA\libraries\Index::getIndexTypes())
) {
$sql_query .= ' USING ' . $type;
}
$parser = $index['Parser'];
if ($index['Index_choice'] == 'FULLTEXT' && ! empty($parser)) {
$sql_query .= " WITH PARSER " . $GLOBALS['dbi']->escapeString($parser);
}
$comment = $index['Index_comment'];
if (! empty($comment)) {
$sql_query .= " COMMENT '" . $GLOBALS['dbi']->escapeString($comment)
. "'";
}
$statement[] = $sql_query;
return $statement;
}
/**
* Statement prefix for the PMA_buildColumnCreationStatement()
*
* @param boolean $is_create_tbl true if requirement is to get the statement
* for table creation
*
* @return string $sql_prefix prefix
*/
function PMA_getStatementPrefix($is_create_tbl = true)
{
$sql_prefix = " ";
if (! $is_create_tbl) {
$sql_prefix = ' ADD ';
}
return $sql_prefix;
}
/**
* Merge index definitions for one type of index
*
* @param array $definitions the index definitions to merge to
* @param boolean $is_create_tbl true if requirement is to get the statement
* for table creation
* @param array $indexed_columns the columns for one type of index
* @param string $index_keyword the index keyword to use in the definition
*
* @return array $index_definitions
*/
function PMA_mergeIndexStatements(
$definitions, $is_create_tbl, $indexed_columns, $index_keyword
) {
foreach ($indexed_columns as $index) {
$statements = PMA_buildIndexStatements(
$index, " " . $index_keyword . " ", $is_create_tbl
);
$definitions = array_merge($definitions, $statements);
}
return $definitions;
}
/**
* Returns sql statement according to the column and index specifications as
* requested
*
* @param boolean $is_create_tbl true if requirement is to get the statement
* for table creation
*
* @return string sql statement
*/
function PMA_getColumnCreationStatements($is_create_tbl = true)
{
$sql_statement = "";
list($field_cnt, $field_primary, $field_index,
$field_unique, $field_fulltext, $field_spatial
) = PMA_getIndexedColumns();
$definitions = PMA_buildColumnCreationStatement(
$field_cnt, $is_create_tbl
);
// Builds the PRIMARY KEY statements
$primary_key_statements = PMA_buildIndexStatements(
isset($field_primary[0]) ? $field_primary[0] : array(),
" PRIMARY KEY ",
$is_create_tbl
);
$definitions = array_merge($definitions, $primary_key_statements);
// Builds the INDEX statements
$definitions = PMA_mergeIndexStatements(
$definitions, $is_create_tbl, $field_index, "INDEX"
);
// Builds the UNIQUE statements
$definitions = PMA_mergeIndexStatements(
$definitions, $is_create_tbl, $field_unique, "UNIQUE"
);
// Builds the FULLTEXT statements
$definitions = PMA_mergeIndexStatements(
$definitions, $is_create_tbl, $field_fulltext, "FULLTEXT"
);
// Builds the SPATIAL statements
$definitions = PMA_mergeIndexStatements(
$definitions, $is_create_tbl, $field_spatial, "SPATIAL"
);
if (count($definitions)) {
$sql_statement = implode(', ', $definitions);
}
$sql_statement = preg_replace('@, $@', '', $sql_statement);
return $sql_statement;
}
/**
* Returns the partitioning clause
*
* @return string partitioning clause
*/
function PMA_getPartitionsDefinition()
{
$sql_query = "";
if (! empty($_REQUEST['partition_by'])
&& ! empty($_REQUEST['partition_expr'])
&& ! empty($_REQUEST['partition_count'])
&& $_REQUEST['partition_count'] > 1
) {
$sql_query .= " PARTITION BY " . $_REQUEST['partition_by']
. " (" . $_REQUEST['partition_expr'] . ")"
. " PARTITIONS " . $_REQUEST['partition_count'];
}
if (! empty($_REQUEST['subpartition_by'])
&& ! empty($_REQUEST['subpartition_expr'])
&& ! empty($_REQUEST['subpartition_count'])
&& $_REQUEST['subpartition_count'] > 1
) {
$sql_query .= " SUBPARTITION BY " . $_REQUEST['subpartition_by']
. " (" . $_REQUEST['subpartition_expr'] . ")"
. " SUBPARTITIONS " . $_REQUEST['subpartition_count'];
}
if (! empty($_REQUEST['partitions'])) {
$i = 0;
$partitions = array();
foreach ($_REQUEST['partitions'] as $partition) {
$partitions[] = PMA_getPartitionDefinition($partition);
$i++;
}
$sql_query .= " (" . implode(", ", $partitions) . ")";
}
return $sql_query;
}
/**
* Returns the definition of a partition/subpartition
*
* @param array $partition array of partition/subpartition detiails
* @param boolean $isSubPartition whether a subpartition
*
* @return string partition/subpartition definition
*/
function PMA_getPartitionDefinition($partition, $isSubPartition = false)
{
$sql_query = " " . ($isSubPartition ? "SUB" : "") . "PARTITION ";
$sql_query .= $partition['name'];
if (! empty($partition['value_type'])) {
$sql_query .= " VALUES " . $partition['value_type'];
if ($partition['value_type'] != 'LESS THAN MAXVALUE') {
$sql_query .= " (" . $partition['value'] . ")";
}
}
if (! empty($partition['engine'])) {
$sql_query .= " ENGINE = " . $partition['engine'];
}
if (! empty($partition['comment'])) {
$sql_query .= " COMMENT = '" . $partition['comment'] . "'";
}
if (! empty($partition['data_directory'])) {
$sql_query .= " DATA DIRECTORY = '" . $partition['data_directory'] . "'";
}
if (! empty($partition['index_directory'])) {
$sql_query .= " INDEX_DIRECTORY = '" . $partition['index_directory'] . "'";
}
if (! empty($partition['max_rows'])) {
$sql_query .= " MAX_ROWS = " . $partition['max_rows'];
}
if (! empty($partition['min_rows'])) {
$sql_query .= " MIN_ROWS = " . $partition['min_rows'];
}
if (! empty($partition['tablespace'])) {
$sql_query .= " TABLESPACE = " . $partition['tablespace'];
}
if (! empty($partition['node_group'])) {
$sql_query .= " NODEGROUP = " . $partition['node_group'];
}
if (! empty($partition['subpartitions'])) {
$j = 0;
$subpartitions = array();
foreach ($partition['subpartitions'] as $subpartition) {
$subpartitions[] = PMA_getPartitionDefinition(
$subpartition,
true
);
$j++;
}
$sql_query .= " (" . implode(", ", $subpartitions) . ")";
}
return $sql_query;
}
/**
* Function to get table creation sql query
*
* @param string $db database name
* @param string $table table name
*
* @return string
*/
function PMA_getTableCreationQuery($db, $table)
{
// get column addition statements
$sql_statement = PMA_getColumnCreationStatements(true);
// Builds the 'create table' statement
$sql_query = 'CREATE TABLE ' . PMA\libraries\Util::backquote($db) . '.'
. PMA\libraries\Util::backquote(trim($table)) . ' (' . $sql_statement . ')';
// Adds table type, character set, comments and partition definition
if (!empty($_REQUEST['tbl_storage_engine'])
&& ($_REQUEST['tbl_storage_engine'] != 'Default')
) {
$sql_query .= ' ENGINE = ' . $_REQUEST['tbl_storage_engine'];
}
if (!empty($_REQUEST['tbl_collation'])) {
$sql_query .= PMA_generateCharsetQueryPart($_REQUEST['tbl_collation']);
}
if (! empty($_REQUEST['connection'])
&& ! empty($_REQUEST['tbl_storage_engine'])
&& $_REQUEST['tbl_storage_engine'] == 'FEDERATED'
) {
$sql_query .= " CONNECTION = '"
. $GLOBALS['dbi']->escapeString($_REQUEST['connection']) . "'";
}
if (!empty($_REQUEST['comment'])) {
$sql_query .= ' COMMENT = \''
. $GLOBALS['dbi']->escapeString($_REQUEST['comment']) . '\'';
}
$sql_query .= PMA_getPartitionsDefinition();
$sql_query .= ';';
return $sql_query;
}
/**
* Function to get the number of fields for the table creation form
*
* @return int
*/
function PMA_getNumberOfFieldsFromRequest()
{
if (isset($_REQUEST['submit_num_fields'])) { // adding new fields
$num_fields = min(
4096,
intval($_REQUEST['orig_num_fields']) + intval($_REQUEST['added_fields'])
);
} elseif (isset($_REQUEST['orig_num_fields'])) { // retaining existing fields
$num_fields = min(4096, intval($_REQUEST['orig_num_fields']));
} elseif (isset($_REQUEST['num_fields'])
&& intval($_REQUEST['num_fields']) > 0
) { // new table with specified number of fields
$num_fields = min(4096, intval($_REQUEST['num_fields']));
} else { // new table with unspecified number of fields
$num_fields = 4;
}
return $num_fields;
}
/**
* Function to execute the column creation statement
*
* @param string $db current database
* @param string $table current table
* @param string $err_url error page url
*
* @return array
*/
function PMA_tryColumnCreationQuery($db, $table, $err_url)
{
// get column addition statements
$sql_statement = PMA_getColumnCreationStatements(false);
// To allow replication, we first select the db to use and then run queries
// on this db.
if (!($GLOBALS['dbi']->selectDb($db))) {
PMA\libraries\Util::mysqlDie(
$GLOBALS['dbi']->getError(),
'USE ' . PMA\libraries\Util::backquote($db), false,
$err_url
);
}
$sql_query = 'ALTER TABLE ' .
PMA\libraries\Util::backquote($table) . ' ' . $sql_statement . ';';
// If there is a request for SQL previewing.
if (isset($_REQUEST['preview_sql'])) {
PMA_previewSQL($sql_query);
}
return array($GLOBALS['dbi']->tryQuery($sql_query) , $sql_query);
}

View File

@ -0,0 +1,89 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Creates the database interface required for database interactions
* and add it to GLOBALS.
*
* @package PhpMyAdmin-DBI
*/
use PMA\libraries\dbi\DBIDummy;
use PMA\libraries\di\Container;
use PMA\libraries\DatabaseInterface;
use PMA\libraries\dbi\DBIMysql;
use PMA\libraries\dbi\DBIMysqli;
if (! defined('PHPMYADMIN')) {
exit;
}
if (defined('TESTSUITE')) {
/**
* For testsuite we use dummy driver which can fake some queries.
*/
$extension = new DBIDummy();
} else {
/**
* First check for the mysqli extension, as it's the one recommended
* for the MySQL server's version that we support
* (if PHP 7+, it's the only one supported)
*/
$extension = 'mysqli';
if (!DatabaseInterface::checkDbExtension($extension)) {
$docurl = PMA\libraries\Util::getDocuLink('faq', 'faqmysql');
$doclink = sprintf(
__('See %sour documentation%s for more information.'),
'[a@' . $docurl . '@documentation]',
'[/a]'
);
if (PHP_VERSION_ID < 70000) {
$extension = 'mysql';
if (! PMA\libraries\DatabaseInterface::checkDbExtension($extension)) {
// warn about both extensions missing and exit
PMA_warnMissingExtension(
'mysqli',
true,
$doclink
);
} elseif (empty($_SESSION['mysqlwarning'])) {
trigger_error(
__(
'You are using the mysql extension which is deprecated in '
. 'phpMyAdmin. Please consider installing the mysqli '
. 'extension.'
) . ' ' . $doclink,
E_USER_WARNING
);
// tell the user just once per session
$_SESSION['mysqlwarning'] = true;
}
} else {
// mysql extension is not part of PHP 7+, so warn and exit
PMA_warnMissingExtension(
'mysqli',
true,
$doclink
);
}
}
/**
* Including The DBI Plugin
*/
switch($extension) {
case 'mysql' :
$extension = new DBIMysql();
break;
case 'mysqli' :
include_once 'libraries/dbi/DBIMysqli.lib.php';
$extension = new DBIMysqli();
break;
}
}
$GLOBALS['dbi'] = new DatabaseInterface($extension);
$container = Container::getDefaultContainer();
$container->set('PMA_DatabaseInterface', $GLOBALS['dbi']);
$container->alias('dbi', 'PMA_DatabaseInterface');

View File

@ -0,0 +1,110 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Common includes for the database level views
*
* @package PhpMyAdmin
*/
use PMA\libraries\Message;
use PMA\libraries\Response;
if (! defined('PHPMYADMIN')) {
exit;
}
/**
* Gets some core libraries
*/
require_once './libraries/bookmark.lib.php';
PMA\libraries\Util::checkParameters(array('db'));
global $cfg;
global $db;
$is_show_stats = $cfg['ShowStats'];
$db_is_system_schema = $GLOBALS['dbi']->isSystemSchema($db);
if ($db_is_system_schema) {
$is_show_stats = false;
}
/**
* Defines the urls to return to in case of error in a sql statement
*/
$err_url_0 = 'index.php' . PMA_URL_getCommon();
$err_url = PMA\libraries\Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabDatabase'], 'database'
)
. PMA_URL_getCommon(array('db' => $db));
/**
* Ensures the database exists (else move to the "parent" script) and displays
* headers
*/
if (! isset($is_db) || ! $is_db) {
if (mb_strlen($db)) {
$is_db = $GLOBALS['dbi']->selectDb($db);
// This "Command out of sync" 2014 error may happen, for example
// after calling a MySQL procedure; at this point we can't select
// the db but it's not necessarily wrong
if ($GLOBALS['dbi']->getError() && $GLOBALS['errno'] == 2014) {
$is_db = true;
unset($GLOBALS['errno']);
}
} else {
$is_db = false;
}
// Not a valid db name -> back to the welcome page
$uri = './index.php'
. PMA_URL_getCommon(array(), 'text')
. (isset($message) ? '&message=' . urlencode($message) : '') . '&reload=1';
if (!mb_strlen($db) || ! $is_db) {
$response = PMA\libraries\Response::getInstance();
if ($response->isAjax()) {
$response->setRequestStatus(false);
$response->addJSON(
'message',
Message::error(__('No databases selected.'))
);
} else {
PMA_sendHeaderLocation($uri);
}
exit;
}
} // end if (ensures db exists)
/**
* Changes database charset if requested by the user
*/
if (isset($_REQUEST['submitcollation'])
&& isset($_REQUEST['db_collation'])
&& ! empty($_REQUEST['db_collation'])
) {
list($db_charset) = explode('_', $_REQUEST['db_collation']);
$sql_query = 'ALTER DATABASE '
. PMA\libraries\Util::backquote($db)
. ' DEFAULT' . PMA_generateCharsetQueryPart($_REQUEST['db_collation']);
$result = $GLOBALS['dbi']->query($sql_query);
$message = Message::success();
unset($db_charset);
/**
* If we are in an Ajax request, let us stop the execution here. Necessary for
* db charset change action on db_operations.php. If this causes a bug on
* other pages, we might have to move this to a different location.
*/
if ($GLOBALS['is_ajax_request'] == true) {
$response = PMA\libraries\Response::getInstance();
$response->setRequestStatus($message->isSuccess());
$response->addJSON('message', $message);
exit;
}
}
/**
* Set parameters for links
*/
$url_query = PMA_URL_getCommon(array('db' => $db));

View File

@ -0,0 +1,406 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Set of functions related to designer
*
* @package PhpMyAdmin
*/
use PMA\libraries\Message;
use PMA\libraries\plugins\SchemaPlugin;
if (! defined('PHPMYADMIN')) {
exit;
}
require_once 'libraries/relation.lib.php';
/**
* Function to get html to display a page selector
*
* @param array $cfgRelation information about the configuration storage
* @param string $db database name
*
* @return string html content
*/
function PMA_getHtmlForPageSelector($cfgRelation, $db)
{
return PMA\libraries\Template::get('database/designer/page_selector')
->render(
array(
'db' => $db,
'cfgRelation' => $cfgRelation
)
);
}
/**
* Function to get html for displaying the page edit/delete form
*
* @param string $db database name
* @param string $operation 'edit' or 'delete' depending on the operation
*
* @return string html content
*/
function PMA_getHtmlForEditOrDeletePages($db, $operation)
{
return PMA\libraries\Template::get('database/designer/edit_delete_pages')
->render(
array(
'db' => $db,
'operation' => $operation
)
);
}
/**
* Function to get html for displaying the page save as form
*
* @param string $db database name
*
* @return string html content
*/
function PMA_getHtmlForPageSaveAs($db)
{
return PMA\libraries\Template::get('database/designer/page_save_as')
->render(
array(
'db' => $db
)
);
}
/**
* Retrieve IDs and names of schema pages
*
* @param string $db database name
*
* @return array array of schema page id and names
*/
function PMA_getPageIdsAndNames($db)
{
$cfgRelation = PMA_getRelationsParam();
$page_query = "SELECT `page_nr`, `page_descr` FROM "
. PMA\libraries\Util::backquote($cfgRelation['db']) . "."
. PMA\libraries\Util::backquote($cfgRelation['pdf_pages'])
. " WHERE db_name = '" . $GLOBALS['dbi']->escapeString($db) . "'"
. " ORDER BY `page_descr`";
$page_rs = PMA_queryAsControlUser(
$page_query, false, PMA\libraries\DatabaseInterface::QUERY_STORE
);
$result = array();
while ($curr_page = $GLOBALS['dbi']->fetchAssoc($page_rs)) {
$result[intval($curr_page['page_nr'])] = $curr_page['page_descr'];
}
return $result;
}
/**
* Function to get html for displaying the schema export
*
* @param string $db database name
* @param int $page the page to be exported
*
* @return string
*/
function PMA_getHtmlForSchemaExport($db, $page)
{
/* Scan for schema plugins */
/* @var $export_list SchemaPlugin[] */
$export_list = PMA_getPlugins(
"schema",
'libraries/plugins/schema/',
null
);
/* Fail if we didn't find any schema plugin */
if (empty($export_list)) {
return Message::error(
__('Could not load schema plugins, please check your installation!')
)->getDisplay();
}
return PMA\libraries\Template::get('database/designer/schema_export')
->render(
array(
'db' => $db,
'page' => $page,
'export_list' => $export_list
)
);
}
/**
* Returns HTML for including some variable to be accessed by JavaScript
*
* @param array $script_tables array on foreign key support for each table
* @param array $script_contr initialization data array
* @param array $script_display_field display fields of each table
* @param int $display_page page number of the selected page
*
* @return string html
*/
function PMA_getHtmlForJSFields(
$script_tables, $script_contr, $script_display_field, $display_page
) {
return PMA\libraries\Template::get('database/designer/js_fields')
->render(
array(
'script_tables' => $script_tables,
'script_contr' => $script_contr,
'script_display_field' => $script_display_field,
'display_page' => $display_page
)
);
}
/**
* Returns HTML for the menu bar of the designer page
*
* @param boolean $visualBuilder whether this is visual query builder
* @param string $selected_page name of the selected page
* @param array $params_array array with class name for various buttons on side
* menu
*
* @return string html
*/
function PMA_getDesignerPageMenu($visualBuilder, $selected_page, $params_array)
{
return PMA\libraries\Template::get('database/designer/side_menu')
->render(
array(
'visualBuilder' => $visualBuilder,
'selected_page' => $selected_page,
'params_array' => $params_array
)
);
}
/**
* Returns array of stored values of Designer Settings
*
* @return array stored values
*/
function PMA_getSideMenuParamsArray()
{
$params = array();
$cfgRelation = PMA_getRelationsParam();
if ($GLOBALS['cfgRelation']['designersettingswork']) {
$query = 'SELECT `settings_data` FROM '
. PMA\libraries\Util::backquote($cfgRelation['db']) . '.'
. PMA\libraries\Util::backquote($cfgRelation['designer_settings'])
. ' WHERE ' . PMA\libraries\Util::backquote('username') . ' = "'
. $GLOBALS['cfg']['Server']['user'] . '";';
$result = $GLOBALS['dbi']->fetchSingleRow($query);
$params = json_decode($result['settings_data'], true);
}
return $params;
}
/**
* Returns class names for various buttons on Designer Side Menu
*
* @return array class names of various buttons
*/
function PMA_returnClassNamesFromMenuButtons()
{
$classes_array = array();
$params_array = PMA_getSideMenuParamsArray();
if (isset($params_array['angular_direct'])
&& $params_array['angular_direct'] == 'angular'
) {
$classes_array['angular_direct'] = 'M_butt_Selected_down';
} else {
$classes_array['angular_direct'] = 'M_butt';
}
if (isset($params_array['snap_to_grid'])
&& $params_array['snap_to_grid'] == 'on'
) {
$classes_array['snap_to_grid'] = 'M_butt_Selected_down';
} else {
$classes_array['snap_to_grid'] = 'M_butt';
}
if (isset($params_array['pin_text'])
&& $params_array['pin_text'] == 'true'
) {
$classes_array['pin_text'] = 'M_butt_Selected_down';
} else {
$classes_array['pin_text'] = 'M_butt';
}
if (isset($params_array['relation_lines'])
&& $params_array['relation_lines'] == 'false'
) {
$classes_array['relation_lines'] = 'M_butt_Selected_down';
} else {
$classes_array['relation_lines'] = 'M_butt';
}
if (isset($params_array['small_big_all'])
&& $params_array['small_big_all'] == 'v'
) {
$classes_array['small_big_all'] = 'M_butt_Selected_down';
} else {
$classes_array['small_big_all'] = 'M_butt';
}
if (isset($params_array['side_menu'])
&& $params_array['side_menu'] == 'true'
) {
$classes_array['side_menu'] = 'M_butt_Selected_down';
} else {
$classes_array['side_menu'] = 'M_butt';
}
return $classes_array;
}
/**
* Returns HTML for the canvas element
*
* @return string html
*/
function PMA_getHTMLCanvas()
{
return PMA\libraries\Template::get('database/designer/canvas')->render();
}
/**
* Return HTML for the table list
*
* @param array $tab_pos table positions
* @param int $display_page page number of the selected page
*
* @return string html
*/
function PMA_getHTMLTableList($tab_pos, $display_page)
{
return PMA\libraries\Template::get('database/designer/table_list')
->render(
array(
'tab_pos' => $tab_pos,
'display_page' => $display_page
)
);
}
/**
* Get HTML to display tables on designer page
*
* @param array $tab_pos tables positions
* @param int $display_page page number of the selected page
* @param array $tab_column table column info
* @param array $tables_all_keys all indices
* @param array $tables_pk_or_unique_keys unique or primary indices
*
* @return string html
*/
function PMA_getDatabaseTables(
$tab_pos, $display_page, $tab_column, $tables_all_keys, $tables_pk_or_unique_keys
) {
return PMA\libraries\Template::get('database/designer/database_tables')
->render(
array(
'tab_pos' => $tab_pos,
'display_page' => $display_page,
'tab_column' => $tab_column,
'tables_all_keys' => $tables_all_keys,
'tables_pk_or_unique_keys' => $tables_pk_or_unique_keys
)
);
}
/**
* Returns HTML for the new relations panel.
*
* @return string html
*/
function PMA_getNewRelationPanel()
{
return PMA\libraries\Template::get('database/designer/new_relation_panel')
->render();
}
/**
* Returns HTML for the relations delete panel
*
* @return string html
*/
function PMA_getDeleteRelationPanel()
{
return PMA\libraries\Template::get('database/designer/delete_relation_panel')
->render();
}
/**
* Returns HTML for the options panel
*
* @return string html
*/
function PMA_getOptionsPanel()
{
return PMA\libraries\Template::get('database/designer/options_panel')->render();
}
/**
* Get HTML for the 'rename to' panel
*
* @return string html
*/
function PMA_getRenameToPanel()
{
return PMA\libraries\Template::get('database/designer/rename_to_panel')
->render();
}
/**
* Returns HTML for the 'having' panel
*
* @return string html
*/
function PMA_getHavingQueryPanel()
{
return PMA\libraries\Template::get('database/designer/having_query_panel')
->render();
}
/**
* Returns HTML for the 'aggregate' panel
*
* @return string html
*/
function PMA_getAggregateQueryPanel()
{
return PMA\libraries\Template::get('database/designer/aggregate_query_panel')
->render();
}
/**
* Returns HTML for the 'where' panel
*
* @return string html
*/
function PMA_getWhereQueryPanel()
{
return PMA\libraries\Template::get('database/designer/where_query_panel')
->render();
}
/**
* Returns HTML for the query details panel
*
* @return string html
*/
function PMA_getQueryDetails()
{
return PMA\libraries\Template::get('database/designer/query_details')->render();
}

View File

@ -0,0 +1,106 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Ensure the database and the table exist (else move to the "parent" script)
* and display headers
*
* @package PhpMyAdmin
*/
use PMA\libraries\Message;
if (! defined('PHPMYADMIN')) {
exit;
}
if (empty($is_db)) {
if (mb_strlen($db)) {
$is_db = @$GLOBALS['dbi']->selectDb($db);
} else {
$is_db = false;
}
if (! $is_db) {
// not a valid db name -> back to the welcome page
if (! defined('IS_TRANSFORMATION_WRAPPER')) {
$response = PMA\libraries\Response::getInstance();
if ($response->isAjax()) {
$response->setRequestStatus(false);
$response->addJSON(
'message',
Message::error(__('No databases selected.'))
);
} else {
$url_params = array('reload' => 1);
if (isset($message)) {
$url_params['message'] = $message;
}
if (! empty($sql_query)) {
$url_params['sql_query'] = $sql_query;
}
if (isset($show_as_php)) {
$url_params['show_as_php'] = $show_as_php;
}
PMA_sendHeaderLocation(
'./index.php'
. PMA_URL_getCommon($url_params, 'text')
);
}
exit;
}
}
} // end if (ensures db exists)
if (empty($is_table)
&& !defined('PMA_SUBMIT_MULT')
&& !defined('TABLE_MAY_BE_ABSENT')
) {
// Not a valid table name -> back to the db_sql.php
if (mb_strlen($table)) {
$is_table = $GLOBALS['dbi']->getCachedTableContent(array($db, $table), false);
if (! $is_table) {
$_result = $GLOBALS['dbi']->tryQuery(
'SHOW TABLES LIKE \''
. $GLOBALS['dbi']->escapeString($table) . '\';',
null, PMA\libraries\DatabaseInterface::QUERY_STORE
);
$is_table = @$GLOBALS['dbi']->numRows($_result);
$GLOBALS['dbi']->freeResult($_result);
}
} else {
$is_table = false;
}
if (! $is_table) {
if (!defined('IS_TRANSFORMATION_WRAPPER')) {
if (mb_strlen($table)) {
// SHOW TABLES doesn't show temporary tables, so try select
// (as it can happen just in case temporary table, it should be
// fast):
/**
* @todo should this check really
* only happen if IS_TRANSFORMATION_WRAPPER?
*/
$_result = $GLOBALS['dbi']->tryQuery(
'SELECT COUNT(*) FROM ' . PMA\libraries\Util::backquote($table)
. ';',
null,
PMA\libraries\DatabaseInterface::QUERY_STORE
);
$is_table = ($_result && @$GLOBALS['dbi']->numRows($_result));
$GLOBALS['dbi']->freeResult($_result);
}
if (! $is_table) {
include './db_sql.php';
exit;
}
}
if (! $is_table) {
exit;
}
}
} // end if (ensures table exists)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
<?php
/**
* Contract for every database extension supported by phpMyAdmin
*
* @package PhpMyAdmin-DBI
*/
/**
* Defines PMA_MYSQL_CLIENT_API if it does not exist based
* on MySQL client version.
*
* @param string $version MySQL version string
*
* @return void
*/
function PMA_defineClientAPI($version)
{
if (!defined('PMA_MYSQL_CLIENT_API')) {
$client_api = explode('.', $version);
define(
'PMA_MYSQL_CLIENT_API',
(int)sprintf(
'%d%02d%02d',
$client_api[0], $client_api[1], intval($client_api[2])
)
);
}
}

View File

@ -0,0 +1,248 @@
<?php
/**
* Contract for every database extension supported by phpMyAdmin
*
* @package PhpMyAdmin-DBI
*/
namespace PMA\libraries\dbi;
require_once 'libraries/dbi/DBIExtension.lib.php';
/**
* Contract for every database extension supported by phpMyAdmin
*
* @package PhpMyAdmin-DBI
*/
interface DBIExtension
{
/**
* connects to the database server
*
* @param string $user user name
* @param string $password user password
* @param bool $is_controluser whether this is a control user connection
* @param array $server host/port/socket/persistent
* @param bool $auxiliary_connection (when true, don't go back to login if
* connection fails)
*
* @return mixed false on error or a connection object on success
*/
public function connect(
$user, $password, $is_controluser = false, $server = null,
$auxiliary_connection = false
);
/**
* selects given database
*
* @param string $dbname database name to select
* @param resource $link connection object
*
* @return boolean
*/
public function selectDb($dbname, $link);
/**
* runs a query and returns the result
*
* @param string $query query to execute
* @param resource $link connection object
* @param int $options query options
*
* @return mixed result
*/
public function realQuery($query, $link, $options);
/**
* Run the multi query and output the results
*
* @param resource $link connection object
* @param string $query multi query statement to execute
*
* @return array|bool
*/
public function realMultiQuery($link, $query);
/**
* returns array of rows with associative and numeric keys from $result
*
* @param object $result result set identifier
*
* @return array
*/
public function fetchArray($result);
/**
* returns array of rows with associative keys from $result
*
* @param object $result result set identifier
*
* @return array
*/
public function fetchAssoc($result);
/**
* returns array of rows with numeric keys from $result
*
* @param object $result result set identifier
*
* @return array
*/
public function fetchRow($result);
/**
* Adjusts the result pointer to an arbitrary row in the result
*
* @param object $result database result
* @param integer $offset offset to seek
*
* @return bool true on success, false on failure
*/
public function dataSeek($result, $offset);
/**
* Frees memory associated with the result
*
* @param object $result database result
*
* @return void
*/
public function freeResult($result);
/**
* Check if there are any more query results from a multi query
*
* @param resource $link the connection object
*
* @return bool true or false
*/
public function moreResults($link);
/**
* Prepare next result from multi_query
*
* @param resource $link the connection object
*
* @return bool true or false
*/
public function nextResult($link);
/**
* Store the result returned from multi query
*
* @param resource $link mysql link
*
* @return mixed false when empty results / result set when not empty
*/
public function storeResult($link);
/**
* Returns a string representing the type of connection used
*
* @param resource $link mysql link
*
* @return string type of connection used
*/
public function getHostInfo($link);
/**
* Returns the version of the MySQL protocol used
*
* @param resource $link mysql link
*
* @return integer version of the MySQL protocol used
*/
public function getProtoInfo($link);
/**
* returns a string that represents the client library version
*
* @return string MySQL client library version
*/
public function getClientInfo();
/**
* returns last error message or false if no errors occurred
*
* @param resource $link connection link
*
* @return string|bool $error or false
*/
public function getError($link);
/**
* returns the number of rows returned by last query
*
* @param object $result result set identifier
*
* @return string|int
*/
public function numRows($result);
/**
* returns the number of rows affected by last query
*
* @param resource $link the connection object
*
* @return int
*/
public function affectedRows($link);
/**
* returns metainfo for fields in $result
*
* @param object $result result set identifier
*
* @return array meta info for fields in $result
*/
public function getFieldsMeta($result);
/**
* return number of fields in given $result
*
* @param object $result result set identifier
*
* @return int field count
*/
public function numFields($result);
/**
* returns the length of the given field $i in $result
*
* @param object $result result set identifier
* @param int $i field
*
* @return int length of field
*/
public function fieldLen($result, $i);
/**
* returns name of $i. field in $result
*
* @param object $result result set identifier
* @param int $i field
*
* @return string name of $i. field in $result
*/
public function fieldName($result, $i);
/**
* returns concatenated string of human readable field flags
*
* @param object $result result set identifier
* @param int $i field
*
* @return string field flags
*/
public function fieldFlags($result, $i);
/**
* returns properly escaped string for use in MySQL queries
*
* @param mixed $link database link
* @param string $str string to be escaped
*
* @return string a MySQL escaped string
*/
public function escapeString($link, $str);
}

View File

@ -0,0 +1,487 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Interface to the classic MySQL extension
*
* @package PhpMyAdmin-DBI
* @subpackage MySQL
*/
namespace PMA\libraries\dbi;
use PMA\libraries\DatabaseInterface;
if (! defined('PHPMYADMIN')) {
exit;
}
if (! extension_loaded('mysql')) {
// The old MySQL extension is deprecated as of PHP 5.5.0, and will be
// removed in the future. Instead, the `MySQLi` or `PDO_MySQL` extension
// should be used.
return;
}
require_once 'libraries/dbi/DBIExtension.lib.php';
/**
* MySQL client API
*/
PMA_defineClientAPI(mysql_get_client_info());
/**
* Interface to the classic MySQL extension
*
* @package PhpMyAdmin-DBI
* @subpackage MySQL
*/
class DBIMysql implements DBIExtension
{
/**
* Helper function for connecting to the database server
*
* @param string $server host/port/socket
* @param string $user mysql user name
* @param string $password mysql user password
* @param int $client_flags client flags of connection
* @param bool $persistent whether to use persistent connection
*
* @return mixed false on error or a mysql connection resource on success
*/
private function _realConnect($server, $user, $password, $client_flags,
$persistent = false
) {
global $cfg;
if (empty($client_flags)) {
if ($cfg['PersistentConnections'] || $persistent) {
$link = @mysql_pconnect($server, $user, $password);
} else {
$link = @mysql_connect($server, $user, $password);
}
} else {
if ($cfg['PersistentConnections'] || $persistent) {
$link = @mysql_pconnect($server, $user, $password, $client_flags);
} else {
$link = @mysql_connect(
$server, $user, $password, false, $client_flags
);
}
}
return $link;
}
/**
* Run the multi query and output the results
*
* @param mysqli $link mysqli object
* @param string $query multi query statement to execute
*
* @return boolean false always false since mysql extension not support
* for multi query executions
*/
public function realMultiQuery($link, $query)
{
// N.B.: PHP's 'mysql' extension does not support
// multi_queries so this function will always
// return false. Use the 'mysqli' extension, if
// you need support for multi_queries.
return false;
}
/**
* connects to the database server
*
* @param string $user mysql user name
* @param string $password mysql user password
* @param bool $is_controluser whether this is a control user connection
* @param array $server host/port/socket/persistent
* @param bool $auxiliary_connection (when true, don't go back to login if
* connection fails)
*
* @return mixed false on error or a mysqli object on success
*/
public function connect(
$user, $password, $is_controluser = false, $server = null,
$auxiliary_connection = false
) {
global $cfg;
$server_port = $GLOBALS['dbi']->getServerPort($server);
$server_socket = $GLOBALS['dbi']->getServerSocket($server);
if ($server_port === 0) {
$server_port = '';
} else {
$server_port = ':' . $server_port;
}
if (is_null($server_socket)) {
$server_socket = '';
} else {
$server_socket = ':' . $server_socket;
}
$client_flags = 0;
if (defined('PMA_ENABLE_LDI')) {
// use CLIENT_LOCAL_FILES as defined in mysql_com.h
// for the case where the client library was not compiled
// with --enable-local-infile
$client_flags |= 128;
}
/* Optionally compress connection */
if (defined('MYSQL_CLIENT_COMPRESS') && $cfg['Server']['compress']) {
$client_flags |= MYSQL_CLIENT_COMPRESS;
}
/* Optionally enable SSL */
if (defined('MYSQL_CLIENT_SSL') && $cfg['Server']['ssl']) {
$client_flags |= MYSQL_CLIENT_SSL;
}
if (! $server) {
$link = $this->_realConnect(
$cfg['Server']['host'] . $server_port . $server_socket,
$user, $password, empty($client_flags) ? null : $client_flags
);
// Retry with empty password if we're allowed to
if (empty($link) && $cfg['Server']['nopassword'] && ! $is_controluser) {
$link = $this->_realConnect(
$cfg['Server']['host'] . $server_port . $server_socket,
$user, '', empty($client_flags) ? null : $client_flags
);
}
} else {
if (!isset($server['host'])) {
$link = $this->_realConnect($server_socket, $user, $password, null);
} else {
$link = $this->_realConnect(
$server['host'] . $server_port . $server_socket,
$user, $password, null
);
}
}
return $link;
}
/**
* selects given database
*
* @param string $dbname name of db to select
* @param resource|null $link mysql link resource
*
* @return bool
*/
public function selectDb($dbname, $link)
{
return mysql_select_db($dbname, $link);
}
/**
* runs a query and returns the result
*
* @param string $query query to run
* @param resource|null $link mysql link resource
* @param int $options query options
*
* @return mixed
*/
public function realQuery($query, $link, $options)
{
if ($options == ($options | DatabaseInterface::QUERY_STORE)) {
return mysql_query($query, $link);
} elseif ($options == ($options | DatabaseInterface::QUERY_UNBUFFERED)) {
return mysql_unbuffered_query($query, $link);
} else {
return mysql_query($query, $link);
}
}
/**
* returns array of rows with associative and numeric keys from $result
*
* @param resource $result result MySQL result
*
* @return array
*/
public function fetchArray($result)
{
return mysql_fetch_array($result, MYSQL_BOTH);
}
/**
* returns array of rows with associative keys from $result
*
* @param resource $result MySQL result
*
* @return array
*/
public function fetchAssoc($result)
{
return mysql_fetch_array($result, MYSQL_ASSOC);
}
/**
* returns array of rows with numeric keys from $result
*
* @param resource $result MySQL result
*
* @return array
*/
public function fetchRow($result)
{
return mysql_fetch_array($result, MYSQL_NUM);
}
/**
* Adjusts the result pointer to an arbitrary row in the result
*
* @param resource $result database result
* @param integer $offset offset to seek
*
* @return bool true on success, false on failure
*/
public function dataSeek($result, $offset)
{
return mysql_data_seek($result, $offset);
}
/**
* Frees memory associated with the result
*
* @param resource $result database result
*
* @return void
*/
public function freeResult($result)
{
if (is_resource($result) && get_resource_type($result) === 'mysql result') {
mysql_free_result($result);
}
}
/**
* Check if there are any more query results from a multi query
*
* @param resource $link the connection object
*
* @return bool false
*/
public function moreResults($link)
{
// N.B.: PHP's 'mysql' extension does not support
// multi_queries so this function will always
// return false. Use the 'mysqli' extension, if
// you need support for multi_queries.
return false;
}
/**
* Prepare next result from multi_query
*
* @param resource $link the connection object
*
* @return boolean false
*/
public function nextResult($link)
{
// N.B.: PHP's 'mysql' extension does not support
// multi_queries so this function will always
// return false. Use the 'mysqli' extension, if
// you need support for multi_queries.
return false;
}
/**
* Returns a string representing the type of connection used
*
* @param resource|null $link mysql link
*
* @return string type of connection used
*/
public function getHostInfo($link)
{
return mysql_get_host_info($link);
}
/**
* Returns the version of the MySQL protocol used
*
* @param resource|null $link mysql link
*
* @return int version of the MySQL protocol used
*/
public function getProtoInfo($link)
{
return mysql_get_proto_info($link);
}
/**
* returns a string that represents the client library version
*
* @return string MySQL client library version
*/
public function getClientInfo()
{
return mysql_get_client_info();
}
/**
* returns last error message or false if no errors occurred
*
* @param resource|null $link mysql link
*
* @return string|bool $error or false
*/
public function getError($link)
{
$GLOBALS['errno'] = 0;
if (null !== $link && false !== $link) {
$error_number = mysql_errno($link);
$error_message = mysql_error($link);
} else {
$error_number = mysql_errno();
$error_message = mysql_error();
}
if (0 == $error_number) {
return false;
}
// keep the error number for further check after
// the call to getError()
$GLOBALS['errno'] = $error_number;
return $GLOBALS['dbi']->formatError($error_number, $error_message);
}
/**
* returns the number of rows returned by last query
*
* @param resource $result MySQL result
*
* @return string|int
*/
public function numRows($result)
{
if (is_bool($result)) {
return 0;
}
return mysql_num_rows($result);
}
/**
* returns the number of rows affected by last query
*
* @param resource|null $link the mysql object
*
* @return int
*/
public function affectedRows($link)
{
return mysql_affected_rows($link);
}
/**
* returns metainfo for fields in $result
*
* @param resource $result MySQL result
*
* @return array meta info for fields in $result
*
* @todo add missing keys like in mysqli_query (decimals)
*/
public function getFieldsMeta($result)
{
$fields = array();
$num_fields = mysql_num_fields($result);
for ($i = 0; $i < $num_fields; $i++) {
$field = mysql_fetch_field($result, $i);
$field->flags = mysql_field_flags($result, $i);
$field->orgtable = mysql_field_table($result, $i);
$field->orgname = mysql_field_name($result, $i);
$fields[] = $field;
}
return $fields;
}
/**
* return number of fields in given $result
*
* @param resource $result MySQL result
*
* @return int field count
*/
public function numFields($result)
{
return mysql_num_fields($result);
}
/**
* returns the length of the given field $i in $result
*
* @param resource $result MySQL result
* @param int $i field
*
* @return int length of field
*/
public function fieldLen($result, $i)
{
return mysql_field_len($result, $i);
}
/**
* returns name of $i. field in $result
*
* @param resource $result MySQL result
* @param int $i field
*
* @return string name of $i. field in $result
*/
public function fieldName($result, $i)
{
return mysql_field_name($result, $i);
}
/**
* returns concatenated string of human readable field flags
*
* @param resource $result MySQL result
* @param int $i field
*
* @return string field flags
*/
public function fieldFlags($result, $i)
{
return mysql_field_flags($result, $i);
}
/**
* Store the result returned from multi query
*
* @param resource $result MySQL result
*
* @return false
*/
public function storeResult($result)
{
return false;
}
/**
* returns properly escaped string for use in MySQL queries
*
* @param mixed $link database link
* @param string $str string to be escaped
*
* @return string a MySQL escaped string
*/
public function escapeString($link, $str)
{
return mysql_real_escape_string($str, $link);
}
}

Some files were not shown because too many files have changed in this diff Show More