Update website
This commit is contained in:
parent
a0b0d3dae7
commit
ae7ef6ad45
3151 changed files with 566766 additions and 48 deletions
207
admin/phpMyAdmin/libraries/classes/Server/Plugin.php
Normal file
207
admin/phpMyAdmin/libraries/classes/Server/Plugin.php
Normal file
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
/**
|
||||
* Server Plugin value object
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server;
|
||||
|
||||
/**
|
||||
* Server Plugin value object
|
||||
*/
|
||||
final class Plugin
|
||||
{
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var string|null */
|
||||
private $version;
|
||||
|
||||
/** @var string */
|
||||
private $status;
|
||||
|
||||
/** @var string */
|
||||
private $type;
|
||||
|
||||
/** @var string|null */
|
||||
private $typeVersion;
|
||||
|
||||
/** @var string|null */
|
||||
private $library;
|
||||
|
||||
/** @var string|null */
|
||||
private $libraryVersion;
|
||||
|
||||
/** @var string|null */
|
||||
private $author;
|
||||
|
||||
/** @var string|null */
|
||||
private $description;
|
||||
|
||||
/** @var string */
|
||||
private $license;
|
||||
|
||||
/** @var string|null */
|
||||
private $loadOption;
|
||||
|
||||
/** @var string|null */
|
||||
private $maturity;
|
||||
|
||||
/** @var string|null */
|
||||
private $authVersion;
|
||||
|
||||
/**
|
||||
* @param string $name Name of the plugin
|
||||
* @param string|null $version Version from the plugin's general type descriptor
|
||||
* @param string $status Plugin status
|
||||
* @param string $type Type of plugin
|
||||
* @param string|null $typeVersion Version from the plugin's type-specific descriptor
|
||||
* @param string|null $library Plugin's shared object file name
|
||||
* @param string|null $libraryVersion Version from the plugin's API interface
|
||||
* @param string|null $author Author of the plugin
|
||||
* @param string|null $description Description
|
||||
* @param string $license Plugin's licence
|
||||
* @param string|null $loadOption How the plugin was loaded
|
||||
* @param string|null $maturity Plugin's maturity level
|
||||
* @param string|null $authVersion Plugin's version as determined by the plugin author
|
||||
*/
|
||||
private function __construct(
|
||||
string $name,
|
||||
?string $version,
|
||||
string $status,
|
||||
string $type,
|
||||
?string $typeVersion,
|
||||
?string $library,
|
||||
?string $libraryVersion,
|
||||
?string $author,
|
||||
?string $description,
|
||||
string $license,
|
||||
?string $loadOption,
|
||||
?string $maturity,
|
||||
?string $authVersion
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->version = $version;
|
||||
$this->status = $status;
|
||||
$this->type = $type;
|
||||
$this->typeVersion = $typeVersion;
|
||||
$this->library = $library;
|
||||
$this->libraryVersion = $libraryVersion;
|
||||
$this->author = $author;
|
||||
$this->description = $description;
|
||||
$this->license = $license;
|
||||
$this->loadOption = $loadOption;
|
||||
$this->maturity = $maturity;
|
||||
$this->authVersion = $authVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $state array with the properties
|
||||
*/
|
||||
public static function fromState(array $state): self
|
||||
{
|
||||
return new self(
|
||||
$state['name'] ?? '',
|
||||
$state['version'] ?? null,
|
||||
$state['status'] ?? '',
|
||||
$state['type'] ?? '',
|
||||
$state['typeVersion'] ?? null,
|
||||
$state['library'] ?? null,
|
||||
$state['libraryVersion'] ?? null,
|
||||
$state['author'] ?? null,
|
||||
$state['description'] ?? null,
|
||||
$state['license'] ?? '',
|
||||
$state['loadOption'] ?? null,
|
||||
$state['maturity'] ?? null,
|
||||
$state['authVersion'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->getName(),
|
||||
'version' => $this->getVersion(),
|
||||
'status' => $this->getStatus(),
|
||||
'type' => $this->getType(),
|
||||
'type_version' => $this->getTypeVersion(),
|
||||
'library' => $this->getLibrary(),
|
||||
'library_version' => $this->getLibraryVersion(),
|
||||
'author' => $this->getAuthor(),
|
||||
'description' => $this->getDescription(),
|
||||
'license' => $this->getLicense(),
|
||||
'load_option' => $this->getLoadOption(),
|
||||
'maturity' => $this->getMaturity(),
|
||||
'auth_version' => $this->getAuthVersion(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getVersion(): ?string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function getStatus(): string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getTypeVersion(): ?string
|
||||
{
|
||||
return $this->typeVersion;
|
||||
}
|
||||
|
||||
public function getLibrary(): ?string
|
||||
{
|
||||
return $this->library;
|
||||
}
|
||||
|
||||
public function getLibraryVersion(): ?string
|
||||
{
|
||||
return $this->libraryVersion;
|
||||
}
|
||||
|
||||
public function getAuthor(): ?string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getLicense(): string
|
||||
{
|
||||
return $this->license;
|
||||
}
|
||||
|
||||
public function getLoadOption(): ?string
|
||||
{
|
||||
return $this->loadOption;
|
||||
}
|
||||
|
||||
public function getMaturity(): ?string
|
||||
{
|
||||
return $this->maturity;
|
||||
}
|
||||
|
||||
public function getAuthVersion(): ?string
|
||||
{
|
||||
return $this->authVersion;
|
||||
}
|
||||
}
|
64
admin/phpMyAdmin/libraries/classes/Server/Plugins.php
Normal file
64
admin/phpMyAdmin/libraries/classes/Server/Plugins.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
|
||||
class Plugins
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/**
|
||||
* @param DatabaseInterface $dbi DatabaseInterface instance
|
||||
*/
|
||||
public function __construct(DatabaseInterface $dbi)
|
||||
{
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Plugin[]
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
$sql = 'SHOW PLUGINS';
|
||||
if (! $cfg['Server']['DisableIS']) {
|
||||
$sql = 'SELECT * FROM information_schema.PLUGINS ORDER BY PLUGIN_TYPE, PLUGIN_NAME';
|
||||
}
|
||||
$result = $this->dbi->query($sql);
|
||||
$plugins = [];
|
||||
while ($row = $this->dbi->fetchAssoc($result)) {
|
||||
$plugins[] = $this->mapRowToPlugin($row);
|
||||
}
|
||||
$this->dbi->freeResult($result);
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row Row fetched from database
|
||||
*/
|
||||
private function mapRowToPlugin(array $row): Plugin
|
||||
{
|
||||
return Plugin::fromState([
|
||||
'name' => $row['PLUGIN_NAME'] ?? $row['Name'],
|
||||
'version' => $row['PLUGIN_VERSION'] ?? null,
|
||||
'status' => $row['PLUGIN_STATUS'] ?? $row['Status'],
|
||||
'type' => $row['PLUGIN_TYPE'] ?? $row['Type'],
|
||||
'typeVersion' => $row['PLUGIN_TYPE_VERSION'] ?? null,
|
||||
'library' => $row['PLUGIN_LIBRARY'] ?? $row['Library'] ?? null,
|
||||
'libraryVersion' => $row['PLUGIN_LIBRARY_VERSION'] ?? null,
|
||||
'author' => $row['PLUGIN_AUTHOR'] ?? null,
|
||||
'description' => $row['PLUGIN_DESCRIPTION'] ?? null,
|
||||
'license' => $row['PLUGIN_LICENSE'] ?? $row['License'],
|
||||
'loadOption' => $row['LOAD_OPTION'] ?? null,
|
||||
'maturity' => $row['PLUGIN_MATURITY'] ?? null,
|
||||
'authVersion' => $row['PLUGIN_AUTH_VERSION'] ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
3989
admin/phpMyAdmin/libraries/classes/Server/Privileges.php
Normal file
3989
admin/phpMyAdmin/libraries/classes/Server/Privileges.php
Normal file
File diff suppressed because it is too large
Load diff
130
admin/phpMyAdmin/libraries/classes/Server/Select.php
Normal file
130
admin/phpMyAdmin/libraries/classes/Server/Select.php
Normal file
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
/**
|
||||
* Code for displaying server selection
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server;
|
||||
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use function count;
|
||||
use function htmlspecialchars;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* Displays the MySQL servers choice form
|
||||
*/
|
||||
class Select
|
||||
{
|
||||
/**
|
||||
* Renders the server selection in list or selectbox form, or option tags only
|
||||
*
|
||||
* @param bool $not_only_options whether to include form tags or not
|
||||
* @param bool $omit_fieldset whether to omit fieldset tag or not
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function render($not_only_options, $omit_fieldset)
|
||||
{
|
||||
$retval = '';
|
||||
|
||||
// Show as list?
|
||||
if ($not_only_options) {
|
||||
$list = $GLOBALS['cfg']['DisplayServersList'];
|
||||
$not_only_options = ! $list;
|
||||
} else {
|
||||
$list = false;
|
||||
}
|
||||
|
||||
if ($not_only_options) {
|
||||
$retval .= '<form method="post" action="'
|
||||
. Util::getScriptNameForOption(
|
||||
$GLOBALS['cfg']['DefaultTabServer'],
|
||||
'server'
|
||||
)
|
||||
. '" class="disableAjax">';
|
||||
|
||||
if (! $omit_fieldset) {
|
||||
$retval .= '<fieldset>';
|
||||
}
|
||||
|
||||
$retval .= Url::getHiddenFields([]);
|
||||
$retval .= '<label for="select_server">'
|
||||
. __('Current server:') . '</label> ';
|
||||
|
||||
$retval .= '<select name="server" id="select_server" class="autosubmit">';
|
||||
$retval .= '<option value="">(' . __('Servers') . ') ...</option>' . "\n";
|
||||
} elseif ($list) {
|
||||
$retval .= __('Current server:') . '<br>';
|
||||
$retval .= '<ul id="list_server">';
|
||||
}
|
||||
|
||||
foreach ($GLOBALS['cfg']['Servers'] as $key => $server) {
|
||||
if (empty($server['host'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! empty($GLOBALS['server']) && (int) $GLOBALS['server'] === (int) $key) {
|
||||
$selected = 1;
|
||||
} else {
|
||||
$selected = 0;
|
||||
}
|
||||
if (! empty($server['verbose'])) {
|
||||
$label = $server['verbose'];
|
||||
} else {
|
||||
$label = $server['host'];
|
||||
if (! empty($server['port'])) {
|
||||
$label .= ':' . $server['port'];
|
||||
}
|
||||
}
|
||||
if (! empty($server['only_db'])) {
|
||||
if (! is_array($server['only_db'])) {
|
||||
$label .= ' - ' . $server['only_db'];
|
||||
// try to avoid displaying a too wide selector
|
||||
} elseif (count($server['only_db']) < 4) {
|
||||
$label .= ' - ' . implode(', ', $server['only_db']);
|
||||
}
|
||||
}
|
||||
if (! empty($server['user']) && $server['auth_type'] === 'config') {
|
||||
$label .= ' (' . $server['user'] . ')';
|
||||
}
|
||||
|
||||
if ($list) {
|
||||
$retval .= '<li>';
|
||||
if ($selected) {
|
||||
$retval .= '<strong>' . htmlspecialchars($label) . '</strong>';
|
||||
} else {
|
||||
$scriptName = Util::getScriptNameForOption(
|
||||
$GLOBALS['cfg']['DefaultTabServer'],
|
||||
'server'
|
||||
);
|
||||
$retval .= '<a class="disableAjax item" href="'
|
||||
. $scriptName
|
||||
. Url::getCommon(['server' => $key], strpos($scriptName, '?') === false ? '?' : '&')
|
||||
. '" >' . htmlspecialchars($label) . '</a>';
|
||||
}
|
||||
$retval .= '</li>';
|
||||
} else {
|
||||
$retval .= '<option value="' . $key . '" '
|
||||
. ($selected ? ' selected="selected"' : '') . '>'
|
||||
. htmlspecialchars($label) . '</option>' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($not_only_options) {
|
||||
$retval .= '</select>';
|
||||
if (! $omit_fieldset) {
|
||||
$retval .= '</fieldset>';
|
||||
}
|
||||
$retval .= '</form>';
|
||||
} elseif ($list) {
|
||||
$retval .= '</ul>';
|
||||
}
|
||||
|
||||
return $retval;
|
||||
}
|
||||
}
|
468
admin/phpMyAdmin/libraries/classes/Server/Status/Data.php
Normal file
468
admin/phpMyAdmin/libraries/classes/Server/Status/Data.php
Normal file
|
@ -0,0 +1,468 @@
|
|||
<?php
|
||||
/**
|
||||
* PhpMyAdmin\Server\Status\Data class
|
||||
* Used by server_status_*.php pages
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\Status;
|
||||
|
||||
use PhpMyAdmin\ReplicationInfo;
|
||||
use PhpMyAdmin\Url;
|
||||
use function basename;
|
||||
use function mb_strpos;
|
||||
use function mb_strtolower;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class Data
|
||||
{
|
||||
/** @var array */
|
||||
public $status;
|
||||
|
||||
/** @var array */
|
||||
public $sections;
|
||||
|
||||
/** @var array */
|
||||
public $variables;
|
||||
|
||||
/** @var array */
|
||||
public $usedQueries;
|
||||
|
||||
/** @var array */
|
||||
public $allocationMap;
|
||||
|
||||
/** @var array */
|
||||
public $links;
|
||||
|
||||
/** @var bool */
|
||||
public $dbIsLocal;
|
||||
|
||||
/** @var mixed */
|
||||
public $section;
|
||||
|
||||
/** @var array */
|
||||
public $sectionUsed;
|
||||
|
||||
/** @var string */
|
||||
public $selfUrl;
|
||||
|
||||
/** @var bool */
|
||||
public $dataLoaded;
|
||||
|
||||
/** @var ReplicationInfo */
|
||||
private $replicationInfo;
|
||||
|
||||
public function getReplicationInfo(): ReplicationInfo
|
||||
{
|
||||
return $this->replicationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [
|
||||
// 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 [
|
||||
// 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()
|
||||
{
|
||||
$primaryInfo = $this->replicationInfo->getPrimaryInfo();
|
||||
$replicaInfo = $this->replicationInfo->getReplicaInfo();
|
||||
|
||||
$links = [];
|
||||
// variable or section name => (name => url)
|
||||
|
||||
$links['table'][__('Flush (close) all tables')] = [
|
||||
'url' => $this->selfUrl,
|
||||
'params' => Url::getCommon(['flush' => 'TABLES'], ''),
|
||||
];
|
||||
$links['table'][__('Show open tables')] = [
|
||||
'url' => Url::getFromRoute('/sql'),
|
||||
'params' => Url::getCommon([
|
||||
'sql_query' => 'SHOW OPEN TABLES',
|
||||
'goto' => $this->selfUrl,
|
||||
], ''),
|
||||
];
|
||||
|
||||
if ($primaryInfo['status']) {
|
||||
$links['repl'][__('Show slave hosts')] = [
|
||||
'url' => Url::getFromRoute('/sql'),
|
||||
'params' => Url::getCommon([
|
||||
'sql_query' => 'SHOW SLAVE HOSTS',
|
||||
'goto' => $this->selfUrl,
|
||||
], ''),
|
||||
];
|
||||
$links['repl'][__('Show master status')] = [
|
||||
'url' => '#replication_master',
|
||||
'params' => '',
|
||||
];
|
||||
}
|
||||
if ($replicaInfo['status']) {
|
||||
$links['repl'][__('Show slave status')] = [
|
||||
'url' => '#replication_slave',
|
||||
'params' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$links['repl']['doc'] = 'replication';
|
||||
|
||||
$links['qcache'][__('Flush query cache')] = [
|
||||
'url' => $this->selfUrl,
|
||||
'params' => Url::getCommon(['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')] = [
|
||||
'url' => Url::getFromRoute('/server/engines/InnoDB'),
|
||||
'params' => '',
|
||||
];
|
||||
$links['innodb'][__('InnoDB Status')] = [
|
||||
'url' => Url::getFromRoute('/server/engines/InnoDB/Status'),
|
||||
'params' => '',
|
||||
];
|
||||
$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
|
||||
*/
|
||||
private function calculateValues(array $server_status, array $server_variables)
|
||||
{
|
||||
// Key_buffer_fraction
|
||||
if (isset($server_status['Key_blocks_unused'], $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'], $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'], $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'], $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'], $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(
|
||||
array $server_status,
|
||||
array $allocations,
|
||||
array $allocationMap,
|
||||
array $sectionUsed,
|
||||
array $used_queries
|
||||
) {
|
||||
foreach ($server_status as $name => $value) {
|
||||
$section_found = false;
|
||||
foreach ($allocations as $filter => $section) {
|
||||
if (mb_strpos($name, $filter) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$allocationMap[$name] = 'other';
|
||||
$sectionUsed['other'] = true;
|
||||
}
|
||||
|
||||
return [
|
||||
$allocationMap,
|
||||
$sectionUsed,
|
||||
$used_queries,
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$this->replicationInfo = new ReplicationInfo($dbi);
|
||||
$this->replicationInfo->load($_POST['master_connection'] ?? null);
|
||||
|
||||
$this->selfUrl = basename($GLOBALS['PMA_PHP_SELF']);
|
||||
|
||||
// get status from server
|
||||
$server_status_result = $dbi->tryQuery('SHOW GLOBAL STATUS');
|
||||
$server_status = [];
|
||||
if ($server_status_result === false) {
|
||||
$this->dataLoaded = false;
|
||||
} else {
|
||||
$this->dataLoaded = true;
|
||||
while ($arr = $dbi->fetchRow($server_status_result)) {
|
||||
$server_status[$arr[0]] = $arr[1];
|
||||
}
|
||||
$dbi->freeResult($server_status_result);
|
||||
}
|
||||
|
||||
// for some calculations we require also some server settings
|
||||
$server_variables = $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 = [];
|
||||
|
||||
// Variable to map variable names to their respective section name
|
||||
// (used for js category filtering)
|
||||
$allocationMap = [];
|
||||
|
||||
// Variable to mark used sections
|
||||
$sectionUsed = [];
|
||||
|
||||
// sort vars into arrays
|
||||
[
|
||||
$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->dbIsLocal = false;
|
||||
// can be null if $cfg['ServerDefault'] = 0;
|
||||
$serverHostToLower = mb_strtolower(
|
||||
(string) $GLOBALS['cfg']['Server']['host']
|
||||
);
|
||||
if ($serverHostToLower === 'localhost'
|
||||
|| $GLOBALS['cfg']['Server']['host'] === '127.0.0.1'
|
||||
|| $GLOBALS['cfg']['Server']['host'] === '::1'
|
||||
) {
|
||||
$this->dbIsLocal = true;
|
||||
}
|
||||
$this->status = $server_status;
|
||||
$this->sections = $sections;
|
||||
$this->variables = $server_variables;
|
||||
$this->usedQueries = $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(array $server_status)
|
||||
{
|
||||
$deprecated = [
|
||||
'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], $server_status[$new])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($server_status[$old]);
|
||||
}
|
||||
|
||||
return $server_status;
|
||||
}
|
||||
}
|
557
admin/phpMyAdmin/libraries/classes/Server/Status/Monitor.php
Normal file
557
admin/phpMyAdmin/libraries/classes/Server/Status/Monitor.php
Normal file
|
@ -0,0 +1,557 @@
|
|||
<?php
|
||||
/**
|
||||
* functions for displaying server status sub item: monitor
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Profiling;
|
||||
use PhpMyAdmin\Server\SysInfo\SysInfo;
|
||||
use PhpMyAdmin\Util;
|
||||
use function array_sum;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function is_numeric;
|
||||
use function json_decode;
|
||||
use function mb_strlen;
|
||||
use function mb_strpos;
|
||||
use function mb_strtolower;
|
||||
use function mb_substr;
|
||||
use function microtime;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* functions for displaying server status sub item: monitor
|
||||
*/
|
||||
class Monitor
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/**
|
||||
* @param DatabaseInterface $dbi DatabaseInterface instance
|
||||
*/
|
||||
public function __construct($dbi)
|
||||
{
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for real-time charting data
|
||||
*
|
||||
* @param string $requiredData Required data
|
||||
*
|
||||
* @return array JSON
|
||||
*/
|
||||
public function getJsonForChartingData(string $requiredData): array
|
||||
{
|
||||
$ret = json_decode($requiredData, true);
|
||||
$statusVars = [];
|
||||
$serverVars = [];
|
||||
$sysinfo = $cpuload = $memory = 0;
|
||||
|
||||
/* Accumulate all required variables and data */
|
||||
[$serverVars, $statusVars, $ret] = $this->getJsonForChartingDataGet(
|
||||
$ret,
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
);
|
||||
|
||||
// Retrieve all required status variables
|
||||
$statusVarValues = [];
|
||||
if (count($statusVars)) {
|
||||
$statusVarValues = $this->dbi->fetchResult(
|
||||
"SHOW GLOBAL STATUS WHERE Variable_name='"
|
||||
. implode("' OR Variable_name='", $statusVars) . "'",
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve all required server variables
|
||||
$serverVarValues = [];
|
||||
if (count($serverVars)) {
|
||||
$serverVarValues = $this->dbi->fetchResult(
|
||||
"SHOW GLOBAL VARIABLES WHERE Variable_name='"
|
||||
. implode("' OR Variable_name='", $serverVars) . "'",
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
// ...and now assign them
|
||||
$ret = $this->getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues);
|
||||
|
||||
$ret['x'] = (int) (microtime(true) * 1000);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the variables for real-time charting data
|
||||
*
|
||||
* @param array $ret Real-time charting data
|
||||
* @param array $statusVarValues Status variable values
|
||||
* @param array $serverVarValues Server variable values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getJsonForChartingDataSet(
|
||||
array $ret,
|
||||
array $statusVarValues,
|
||||
array $serverVarValues
|
||||
): array {
|
||||
foreach ($ret as $chart_id => $chartNodes) {
|
||||
foreach ($chartNodes as $node_id => $nodeDataPoints) {
|
||||
foreach ($nodeDataPoints as $point_id => $dataPoint) {
|
||||
switch ($dataPoint['type']) {
|
||||
case 'statusvar':
|
||||
$ret[$chart_id][$node_id][$point_id]['value']
|
||||
= $statusVarValues[$dataPoint['name']];
|
||||
break;
|
||||
case 'servervar':
|
||||
$ret[$chart_id][$node_id][$point_id]['value']
|
||||
= $serverVarValues[$dataPoint['name']];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get called to get JSON for charting data
|
||||
*
|
||||
* @param array $ret Real-time charting data
|
||||
* @param array $serverVars Server variable values
|
||||
* @param array $statusVars Status variable values
|
||||
* @param mixed $sysinfo System info
|
||||
* @param mixed $cpuload CPU load
|
||||
* @param mixed $memory Memory
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getJsonForChartingDataGet(
|
||||
array $ret,
|
||||
array $serverVars,
|
||||
array $statusVars,
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
) {
|
||||
// For each chart
|
||||
foreach ($ret as $chartId => $chartNodes) {
|
||||
// For each data series
|
||||
foreach ($chartNodes as $nodeId => $nodeDataPoints) {
|
||||
// For each data point in the series (usually just 1)
|
||||
foreach ($nodeDataPoints as $pointId => $dataPoint) {
|
||||
[$serverVars, $statusVars, $ret[$chartId][$nodeId][$pointId]]
|
||||
= $this->getJsonForChartingDataSwitch(
|
||||
$dataPoint['type'],
|
||||
$dataPoint['name'],
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$ret[$chartId][$nodeId][$pointId],
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
);
|
||||
} /* foreach */
|
||||
} /* foreach */
|
||||
}
|
||||
|
||||
return [
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$ret,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch called to get JSON for charting data
|
||||
*
|
||||
* @param string $type Type
|
||||
* @param string $pName Name
|
||||
* @param array $serverVars Server variable values
|
||||
* @param array $statusVars Status variable values
|
||||
* @param array $ret Real-time charting data
|
||||
* @param mixed $sysinfo System info
|
||||
* @param mixed $cpuload CPU load
|
||||
* @param mixed $memory Memory
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getJsonForChartingDataSwitch(
|
||||
$type,
|
||||
$pName,
|
||||
array $serverVars,
|
||||
array $statusVars,
|
||||
array $ret,
|
||||
$sysinfo,
|
||||
$cpuload,
|
||||
$memory
|
||||
) {
|
||||
switch ($type) {
|
||||
/* We only collect the status and server variables here to
|
||||
* read them all in one query,
|
||||
* and only afterwards assign them.
|
||||
* Also do some allow list filtering on the names
|
||||
*/
|
||||
case 'servervar':
|
||||
if (! preg_match('/[^a-zA-Z_]+/', $pName)) {
|
||||
$serverVars[] = $pName;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'statusvar':
|
||||
if (! preg_match('/[^a-zA-Z_]+/', $pName)) {
|
||||
$statusVars[] = $pName;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'proc':
|
||||
$result = $this->dbi->query('SHOW PROCESSLIST');
|
||||
$ret['value'] = $this->dbi->numRows($result);
|
||||
break;
|
||||
|
||||
case 'cpu':
|
||||
if (! $sysinfo) {
|
||||
$sysinfo = SysInfo::get();
|
||||
}
|
||||
if (! $cpuload) {
|
||||
$cpuload = $sysinfo->loadavg();
|
||||
}
|
||||
|
||||
if (SysInfo::getOs() === 'Linux') {
|
||||
$ret['idle'] = $cpuload['idle'];
|
||||
$ret['busy'] = $cpuload['busy'];
|
||||
} else {
|
||||
$ret['value'] = $cpuload['loadavg'];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'memory':
|
||||
if (! $sysinfo) {
|
||||
$sysinfo = SysInfo::get();
|
||||
}
|
||||
if (! $memory) {
|
||||
$memory = $sysinfo->memory();
|
||||
}
|
||||
|
||||
$ret['value'] = $memory[$pName] ?? 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
$serverVars,
|
||||
$statusVars,
|
||||
$ret,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for log data with type: slow
|
||||
*
|
||||
* @param int $start Unix Time: Start time for query
|
||||
* @param int $end Unix Time: End time for query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonForLogDataTypeSlow(int $start, int $end): array
|
||||
{
|
||||
$query = 'SELECT start_time, user_host, ';
|
||||
$query .= 'Sec_to_Time(Sum(Time_to_Sec(query_time))) as query_time, ';
|
||||
$query .= 'Sec_to_Time(Sum(Time_to_Sec(lock_time))) as lock_time, ';
|
||||
$query .= 'SUM(rows_sent) AS rows_sent, ';
|
||||
$query .= 'SUM(rows_examined) AS rows_examined, db, sql_text, ';
|
||||
$query .= 'COUNT(sql_text) AS \'#\' ';
|
||||
$query .= 'FROM `mysql`.`slow_log` ';
|
||||
$query .= 'WHERE start_time > FROM_UNIXTIME(' . $start . ') ';
|
||||
$query .= 'AND start_time < FROM_UNIXTIME(' . $end . ') GROUP BY sql_text';
|
||||
|
||||
$result = $this->dbi->tryQuery($query);
|
||||
|
||||
$return = [
|
||||
'rows' => [],
|
||||
'sum' => [],
|
||||
];
|
||||
|
||||
while ($row = $this->dbi->fetchAssoc($result)) {
|
||||
$type = mb_strtolower(
|
||||
mb_substr(
|
||||
$row['sql_text'],
|
||||
0,
|
||||
(int) mb_strpos($row['sql_text'], ' ')
|
||||
)
|
||||
);
|
||||
|
||||
switch ($type) {
|
||||
case 'insert':
|
||||
case 'update':
|
||||
//Cut off big inserts and updates, but append byte count instead
|
||||
if (mb_strlen($row['sql_text']) > 220) {
|
||||
$implodeSqlText = implode(
|
||||
' ',
|
||||
(array) Util::formatByteDown(
|
||||
mb_strlen($row['sql_text']),
|
||||
2,
|
||||
2
|
||||
)
|
||||
);
|
||||
$row['sql_text'] = mb_substr($row['sql_text'], 0, 200)
|
||||
. '... [' . $implodeSqlText . ']';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (! isset($return['sum'][$type])) {
|
||||
$return['sum'][$type] = 0;
|
||||
}
|
||||
$return['sum'][$type] += $row['#'];
|
||||
$return['rows'][] = $row;
|
||||
}
|
||||
|
||||
$return['sum']['TOTAL'] = array_sum($return['sum']);
|
||||
$return['numRows'] = count($return['rows']);
|
||||
|
||||
$this->dbi->freeResult($result);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSon for log data with type: general
|
||||
*
|
||||
* @param int $start Unix Time: Start time for query
|
||||
* @param int $end Unix Time: End time for query
|
||||
* @param bool $isTypesLimited Whether to limit types or not
|
||||
* @param bool $removeVariables Whether to remove variables or not
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonForLogDataTypeGeneral(
|
||||
int $start,
|
||||
int $end,
|
||||
bool $isTypesLimited,
|
||||
bool $removeVariables
|
||||
): array {
|
||||
$limitTypes = '';
|
||||
if ($isTypesLimited) {
|
||||
$limitTypes = 'AND argument REGEXP \'^(INSERT|SELECT|UPDATE|DELETE)\' ';
|
||||
}
|
||||
|
||||
$query = 'SELECT TIME(event_time) as event_time, user_host, thread_id, ';
|
||||
$query .= 'server_id, argument, count(argument) as \'#\' ';
|
||||
$query .= 'FROM `mysql`.`general_log` ';
|
||||
$query .= 'WHERE command_type=\'Query\' ';
|
||||
$query .= 'AND event_time > FROM_UNIXTIME(' . $start . ') ';
|
||||
$query .= 'AND event_time < FROM_UNIXTIME(' . $end . ') ';
|
||||
$query .= $limitTypes . 'GROUP by argument'; // HAVING count > 1';
|
||||
|
||||
$result = $this->dbi->tryQuery($query);
|
||||
|
||||
$return = [
|
||||
'rows' => [],
|
||||
'sum' => [],
|
||||
];
|
||||
$insertTables = [];
|
||||
$insertTablesFirst = -1;
|
||||
$i = 0;
|
||||
|
||||
while ($row = $this->dbi->fetchAssoc($result)) {
|
||||
preg_match('/^(\w+)\s/', $row['argument'], $match);
|
||||
$type = mb_strtolower($match[1]);
|
||||
|
||||
if (! isset($return['sum'][$type])) {
|
||||
$return['sum'][$type] = 0;
|
||||
}
|
||||
$return['sum'][$type] += $row['#'];
|
||||
|
||||
switch ($type) {
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case 'insert':
|
||||
// Group inserts if selected
|
||||
if ($removeVariables
|
||||
&& preg_match(
|
||||
'/^INSERT INTO (`|\'|"|)([^\s\\1]+)\\1/i',
|
||||
$row['argument'],
|
||||
$matches
|
||||
)
|
||||
) {
|
||||
$insertTables[$matches[2]]++;
|
||||
if ($insertTables[$matches[2]] > 1) {
|
||||
$return['rows'][$insertTablesFirst]['#']
|
||||
= $insertTables[$matches[2]];
|
||||
|
||||
// Add a ... to the end of this query to indicate that
|
||||
// there's been other queries
|
||||
$temp = $return['rows'][$insertTablesFirst]['argument'];
|
||||
$return['rows'][$insertTablesFirst]['argument']
|
||||
.= $this->getSuspensionPoints(
|
||||
$temp[strlen($temp) - 1]
|
||||
);
|
||||
|
||||
// Group this value, thus do not add to the result list
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$insertTablesFirst = $i;
|
||||
$insertTables[$matches[2]] += $row['#'] - 1;
|
||||
}
|
||||
// No break here
|
||||
|
||||
case 'update':
|
||||
// Cut off big inserts and updates,
|
||||
// but append byte count therefor
|
||||
if (mb_strlen($row['argument']) > 220) {
|
||||
$row['argument'] = mb_substr($row['argument'], 0, 200)
|
||||
. '... ['
|
||||
. implode(
|
||||
' ',
|
||||
(array) Util::formatByteDown(
|
||||
mb_strlen($row['argument']),
|
||||
2,
|
||||
2
|
||||
)
|
||||
)
|
||||
. ']';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$return['rows'][] = $row;
|
||||
$i++;
|
||||
}
|
||||
|
||||
$return['sum']['TOTAL'] = array_sum($return['sum']);
|
||||
$return['numRows'] = count($return['rows']);
|
||||
|
||||
$this->dbi->freeResult($result);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return suspension points if needed
|
||||
*
|
||||
* @param string $lastChar Last char
|
||||
*
|
||||
* @return string Return suspension points if needed
|
||||
*/
|
||||
private function getSuspensionPoints(string $lastChar): string
|
||||
{
|
||||
if ($lastChar !== '.') {
|
||||
return '<br>...';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for logging vars
|
||||
*
|
||||
* @param string|null $name Variable name
|
||||
* @param string|null $value Variable value
|
||||
*
|
||||
* @return array JSON
|
||||
*/
|
||||
public function getJsonForLoggingVars(?string $name, ?string $value): array
|
||||
{
|
||||
if (isset($name, $value)) {
|
||||
$escapedValue = $this->dbi->escapeString($value);
|
||||
if (! is_numeric($escapedValue)) {
|
||||
$escapedValue = "'" . $escapedValue . "'";
|
||||
}
|
||||
|
||||
if (! preg_match('/[^a-zA-Z0-9_]+/', $name)) {
|
||||
$this->dbi->query(
|
||||
'SET GLOBAL ' . $name . ' = ' . $escapedValue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dbi->fetchResult(
|
||||
'SHOW GLOBAL VARIABLES WHERE Variable_name IN'
|
||||
. ' ("general_log","slow_query_log","long_query_time","log_output")',
|
||||
0,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON for query_analyzer
|
||||
*
|
||||
* @param string $database Database name
|
||||
* @param string $query SQL query
|
||||
*
|
||||
* @return array JSON
|
||||
*/
|
||||
public function getJsonForQueryAnalyzer(
|
||||
string $database,
|
||||
string $query
|
||||
): array {
|
||||
global $cached_affected_rows;
|
||||
|
||||
$return = [];
|
||||
|
||||
if (strlen($database) > 0) {
|
||||
$this->dbi->selectDb($database);
|
||||
}
|
||||
|
||||
$profiling = Profiling::isSupported($this->dbi);
|
||||
|
||||
if ($profiling) {
|
||||
$this->dbi->query('SET PROFILING=1;');
|
||||
}
|
||||
|
||||
// Do not cache query
|
||||
$sqlQuery = preg_replace(
|
||||
'/^(\s*SELECT)/i',
|
||||
'\\1 SQL_NO_CACHE',
|
||||
$query
|
||||
);
|
||||
|
||||
$this->dbi->tryQuery($sqlQuery);
|
||||
$return['affectedRows'] = $cached_affected_rows;
|
||||
|
||||
$result = $this->dbi->tryQuery('EXPLAIN ' . $sqlQuery);
|
||||
while ($row = $this->dbi->fetchAssoc($result)) {
|
||||
$return['explain'][] = $row;
|
||||
}
|
||||
|
||||
// In case an error happened
|
||||
$return['error'] = $this->dbi->getError();
|
||||
|
||||
$this->dbi->freeResult($result);
|
||||
|
||||
if ($profiling) {
|
||||
$return['profiling'] = [];
|
||||
$result = $this->dbi->tryQuery(
|
||||
'SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING'
|
||||
. ' WHERE QUERY_ID=1 ORDER BY seq'
|
||||
);
|
||||
while ($row = $this->dbi->fetchAssoc($result)) {
|
||||
$return['profiling'][] = $row;
|
||||
}
|
||||
$this->dbi->freeResult($result);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
50
admin/phpMyAdmin/libraries/classes/Server/SysInfo/Base.php
Normal file
50
admin/phpMyAdmin/libraries/classes/Server/SysInfo/Base.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\SysInfo;
|
||||
|
||||
use const PHP_OS;
|
||||
|
||||
/**
|
||||
* Basic SysInfo class not providing any real data.
|
||||
*/
|
||||
class Base
|
||||
{
|
||||
/**
|
||||
* The OS name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $os = PHP_OS;
|
||||
|
||||
/**
|
||||
* Gets load information
|
||||
*
|
||||
* @return array with load data
|
||||
*/
|
||||
public function loadavg()
|
||||
{
|
||||
return ['loadavg' => 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about memory usage
|
||||
*
|
||||
* @return array with memory usage data
|
||||
*/
|
||||
public function memory()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether class is supported in this environment
|
||||
*
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function supported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
119
admin/phpMyAdmin/libraries/classes/Server/SysInfo/Linux.php
Normal file
119
admin/phpMyAdmin/libraries/classes/Server/SysInfo/Linux.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\SysInfo;
|
||||
|
||||
use function array_combine;
|
||||
use function array_merge;
|
||||
use function file_get_contents;
|
||||
use function intval;
|
||||
use function is_array;
|
||||
use function is_readable;
|
||||
use function mb_strpos;
|
||||
use function mb_substr;
|
||||
use function preg_match_all;
|
||||
use function preg_split;
|
||||
|
||||
/**
|
||||
* Linux based SysInfo class
|
||||
*/
|
||||
class Linux extends Base
|
||||
{
|
||||
/**
|
||||
* The OS name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $os = 'Linux';
|
||||
|
||||
/**
|
||||
* Gets load information
|
||||
*
|
||||
* @return array<string, int> with load data
|
||||
*/
|
||||
public function loadavg()
|
||||
{
|
||||
$buf = file_get_contents('/proc/stat');
|
||||
if ($buf === false) {
|
||||
$buf = '';
|
||||
}
|
||||
$pos = mb_strpos($buf, "\n");
|
||||
if ($pos === false) {
|
||||
$pos = 0;
|
||||
}
|
||||
$nums = preg_split(
|
||||
'/\s+/',
|
||||
mb_substr(
|
||||
$buf,
|
||||
0,
|
||||
$pos
|
||||
)
|
||||
);
|
||||
|
||||
if (! is_array($nums)) {
|
||||
return ['busy' => 0, 'idle' => 0];
|
||||
}
|
||||
|
||||
return [
|
||||
'busy' => (int) $nums[1] + (int) $nums[2] + (int) $nums[3],
|
||||
'idle' => (int) $nums[4],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether class is supported in this environment
|
||||
*
|
||||
* @return bool 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
|
||||
*/
|
||||
public function memory()
|
||||
{
|
||||
$content = @file_get_contents('/proc/meminfo');
|
||||
if ($content === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
preg_match_all(
|
||||
SysInfo::MEMORY_REGEXP,
|
||||
$content,
|
||||
$matches
|
||||
);
|
||||
|
||||
$mem = array_combine($matches[1], $matches[2]);
|
||||
if ($mem === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$defaults = [
|
||||
'MemTotal' => 0,
|
||||
'MemFree' => 0,
|
||||
'Cached' => 0,
|
||||
'Buffers' => 0,
|
||||
'SwapTotal' => 0,
|
||||
'SwapFree' => 0,
|
||||
'SwapCached' => 0,
|
||||
];
|
||||
|
||||
$mem = array_merge($defaults, $mem);
|
||||
|
||||
foreach ($mem as $idx => $value) {
|
||||
$mem[$idx] = intval($value);
|
||||
}
|
||||
|
||||
/** @var array<string, int> $mem */
|
||||
$mem['MemUsed'] = $mem['MemTotal'] - $mem['MemFree'] - $mem['Cached'] - $mem['Buffers'];
|
||||
$mem['SwapUsed'] = $mem['SwapTotal'] - $mem['SwapFree'] - $mem['SwapCached'];
|
||||
|
||||
return $mem;
|
||||
}
|
||||
}
|
84
admin/phpMyAdmin/libraries/classes/Server/SysInfo/SunOs.php
Normal file
84
admin/phpMyAdmin/libraries/classes/Server/SysInfo/SunOs.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\SysInfo;
|
||||
|
||||
use function explode;
|
||||
use function is_readable;
|
||||
use function shell_exec;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* SunOS based SysInfo class
|
||||
*/
|
||||
class SunOs extends Base
|
||||
{
|
||||
/**
|
||||
* The OS name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $os = 'SunOS';
|
||||
|
||||
/**
|
||||
* Read value from kstat
|
||||
*
|
||||
* @param string $key Key to read
|
||||
*
|
||||
* @return string with value
|
||||
*/
|
||||
private function kstat($key)
|
||||
{
|
||||
$m = shell_exec('kstat -p d ' . $key);
|
||||
|
||||
if ($m) {
|
||||
[, $value] = explode("\t", trim($m), 2);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets load information
|
||||
*
|
||||
* @return array with load data
|
||||
*/
|
||||
public function loadavg()
|
||||
{
|
||||
$load1 = $this->kstat('unix:0:system_misc:avenrun_1min');
|
||||
|
||||
return ['loadavg' => $load1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether class is supported in this environment
|
||||
*
|
||||
* @return bool 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 = (int) $this->kstat('unix:0:seg_cache:slab_size');
|
||||
$mem = [];
|
||||
$mem['MemTotal'] = (int) $this->kstat('unix:0:system_pages:pagestotal') * $pagesize;
|
||||
$mem['MemUsed'] = (int) $this->kstat('unix:0:system_pages:pageslocked') * $pagesize;
|
||||
$mem['MemFree'] = (int) $this->kstat('unix:0:system_pages:pagesfree') * $pagesize;
|
||||
$mem['SwapTotal'] = (int) $this->kstat('unix:0:vminfo:swap_avail') / 1024;
|
||||
$mem['SwapUsed'] = (int) $this->kstat('unix:0:vminfo:swap_alloc') / 1024;
|
||||
$mem['SwapFree'] = (int) $this->kstat('unix:0:vminfo:swap_free') / 1024;
|
||||
|
||||
return $mem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\SysInfo;
|
||||
|
||||
use const PHP_OS;
|
||||
use function in_array;
|
||||
use function ucfirst;
|
||||
|
||||
/**
|
||||
* Library for extracting information about system memory and cpu.
|
||||
* Currently supports all Windows and Linux platforms
|
||||
*
|
||||
* This code is based on the OS Classes from the phpsysinfo project
|
||||
* (https://phpsysinfo.github.io/phpsysinfo/)
|
||||
*/
|
||||
class SysInfo
|
||||
{
|
||||
public const MEMORY_REGEXP = '/^(MemTotal|MemFree|Cached|Buffers|SwapCached|SwapTotal|SwapFree):\s+(.*)\s*kB/im';
|
||||
|
||||
/**
|
||||
* Returns OS type used for sysinfo class
|
||||
*
|
||||
* @param string $php_os PHP_OS constant
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getOs($php_os = PHP_OS)
|
||||
{
|
||||
// look for common UNIX-like systems
|
||||
$unix_like = [
|
||||
'FreeBSD',
|
||||
'DragonFly',
|
||||
];
|
||||
if (in_array($php_os, $unix_like)) {
|
||||
$php_os = 'Linux';
|
||||
}
|
||||
|
||||
return ucfirst($php_os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets SysInfo class matching current OS
|
||||
*
|
||||
* @return Base sysinfo class
|
||||
*/
|
||||
public static function get()
|
||||
{
|
||||
$php_os = self::getOs();
|
||||
|
||||
switch ($php_os) {
|
||||
case 'Linux':
|
||||
$sysInfo = new Linux();
|
||||
if ($sysInfo->supported()) {
|
||||
return $sysInfo;
|
||||
}
|
||||
break;
|
||||
case 'WINNT':
|
||||
$sysInfo = new WindowsNt();
|
||||
if ($sysInfo->supported()) {
|
||||
return $sysInfo;
|
||||
}
|
||||
break;
|
||||
case 'SunOS':
|
||||
$sysInfo = new SunOs();
|
||||
if ($sysInfo->supported()) {
|
||||
return $sysInfo;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return new Base();
|
||||
}
|
||||
}
|
140
admin/phpMyAdmin/libraries/classes/Server/SysInfo/WindowsNt.php
Normal file
140
admin/phpMyAdmin/libraries/classes/Server/SysInfo/WindowsNt.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server\SysInfo;
|
||||
|
||||
use COM;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* Windows NT based SysInfo class
|
||||
*/
|
||||
class WindowsNt extends Base
|
||||
{
|
||||
/** @var COM|null */
|
||||
private $wmi;
|
||||
|
||||
/**
|
||||
* The OS name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function loadavg()
|
||||
{
|
||||
$sum = 0;
|
||||
$buffer = $this->getWMI('Win32_Processor', ['LoadPercentage']);
|
||||
|
||||
foreach ($buffer as $load) {
|
||||
$value = $load['LoadPercentage'];
|
||||
$sum += $value;
|
||||
}
|
||||
|
||||
return ['loadavg' => $sum / count($buffer)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether class is supported in this environment
|
||||
*
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function supported()
|
||||
{
|
||||
return $this->wmi !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from WMI
|
||||
*
|
||||
* @param string $strClass Class to read
|
||||
* @param array $strValue Values to read
|
||||
*
|
||||
* @return array with results
|
||||
*/
|
||||
private function getWMI($strClass, array $strValue = [])
|
||||
{
|
||||
$arrData = [];
|
||||
|
||||
$objWEBM = $this->wmi->Get($strClass);
|
||||
$arrProp = $objWEBM->Properties_;
|
||||
$arrWEBMCol = $objWEBM->Instances_();
|
||||
foreach ($arrWEBMCol as $objItem) {
|
||||
$arrInstance = [];
|
||||
foreach ($arrProp as $propItem) {
|
||||
$name = $propItem->Name;
|
||||
if (! empty($strValue) && ! in_array($name, $strValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $objItem->$name;
|
||||
if (is_string($value)) {
|
||||
$arrInstance[$name] = trim($value);
|
||||
} else {
|
||||
$arrInstance[$name] = $value;
|
||||
}
|
||||
}
|
||||
$arrData[] = $arrInstance;
|
||||
}
|
||||
|
||||
return $arrData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about memory usage
|
||||
*
|
||||
* @return array with memory usage data
|
||||
*/
|
||||
public function memory()
|
||||
{
|
||||
$buffer = $this->getWMI(
|
||||
'Win32_OperatingSystem',
|
||||
[
|
||||
'TotalVisibleMemorySize',
|
||||
'FreePhysicalMemory',
|
||||
]
|
||||
);
|
||||
$mem = [];
|
||||
$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;
|
||||
}
|
||||
}
|
358
admin/phpMyAdmin/libraries/classes/Server/UserGroups.php
Normal file
358
admin/phpMyAdmin/libraries/classes/Server/UserGroups.php
Normal file
|
@ -0,0 +1,358 @@
|
|||
<?php
|
||||
/**
|
||||
* set of functions for user group handling
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server;
|
||||
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Relation;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use function htmlspecialchars;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function mb_substr;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* PhpMyAdmin\Server\UserGroups class
|
||||
*/
|
||||
class UserGroups
|
||||
{
|
||||
/**
|
||||
* Return HTML to list the users belonging to a given user group
|
||||
*
|
||||
* @param string $userGroup user group name
|
||||
*
|
||||
* @return string HTML to list the users belonging to a given user group
|
||||
*/
|
||||
public static function getHtmlForListingUsersofAGroup(string $userGroup): string
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$users = [];
|
||||
$numRows = 0;
|
||||
$relation = new Relation($dbi);
|
||||
|
||||
$userGroupSpecialChars = htmlspecialchars($userGroup);
|
||||
$cfgRelation = $relation->getRelationsParam();
|
||||
$usersTable = Util::backquote($cfgRelation['db'])
|
||||
. '.' . Util::backquote($cfgRelation['users']);
|
||||
$sql_query = 'SELECT `username` FROM ' . $usersTable
|
||||
. " WHERE `usergroup`='" . $dbi->escapeString($userGroup)
|
||||
. "'";
|
||||
$result = $relation->queryAsControlUser($sql_query, false);
|
||||
if ($result) {
|
||||
$numRows = $dbi->numRows($result);
|
||||
if ($numRows != 0) {
|
||||
$i = 0;
|
||||
while ($row = $dbi->fetchRow($result)) {
|
||||
$i++;
|
||||
$user = [];
|
||||
$user['count'] = $i;
|
||||
$user['user'] = $row[0];
|
||||
$users[] = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
$dbi->freeResult($result);
|
||||
$template = new Template();
|
||||
|
||||
return $template->render('server/user_groups/user_listings', [
|
||||
'user_group_special_chars' => $userGroupSpecialChars,
|
||||
'num_rows' => $numRows,
|
||||
'users' => $users,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for the 'user groups' table
|
||||
*
|
||||
* @return string HTML for the 'user groups' table
|
||||
*/
|
||||
public static function getHtmlForUserGroupsTable(): string
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$relation = new Relation($dbi);
|
||||
$cfgRelation = $relation->getRelationsParam();
|
||||
$groupTable = Util::backquote($cfgRelation['db'])
|
||||
. '.' . Util::backquote($cfgRelation['usergroups']);
|
||||
$sql_query = 'SELECT * FROM ' . $groupTable . ' ORDER BY `usergroup` ASC';
|
||||
$result = $relation->queryAsControlUser($sql_query, false);
|
||||
$numRows = $dbi->numRows($result);
|
||||
$userGroups = [];
|
||||
$userGroupsValues = [];
|
||||
$action = Url::getFromRoute('/server/privileges');
|
||||
$hidden_inputs = null;
|
||||
if ($result && $numRows) {
|
||||
$hidden_inputs = Url::getHiddenInputs();
|
||||
while ($row = $dbi->fetchAssoc($result)) {
|
||||
$groupName = $row['usergroup'];
|
||||
if (! isset($userGroups[$groupName])) {
|
||||
$userGroups[$groupName] = [];
|
||||
}
|
||||
$userGroups[$groupName][$row['tab']] = $row['allowed'];
|
||||
}
|
||||
|
||||
foreach ($userGroups as $groupName => $tabs) {
|
||||
$userGroupVal = [];
|
||||
$userGroupVal['name'] = htmlspecialchars((string) $groupName);
|
||||
$userGroupVal['serverTab'] = self::getAllowedTabNames($tabs, 'server');
|
||||
$userGroupVal['dbTab'] = self::getAllowedTabNames($tabs, 'db');
|
||||
$userGroupVal['tableTab'] = self::getAllowedTabNames($tabs, 'table');
|
||||
$userGroupVal['userGroupUrl'] = Url::getFromRoute('/server/user-groups');
|
||||
$userGroupVal['viewUsersUrl'] = Url::getCommon(
|
||||
[
|
||||
'viewUsers' => 1,
|
||||
'userGroup' => $groupName,
|
||||
],
|
||||
''
|
||||
);
|
||||
$userGroupVal['viewUsersIcon'] = Generator::getIcon('b_usrlist', __('View users'));
|
||||
|
||||
$userGroupVal['editUsersUrl'] = Url::getCommon(
|
||||
[
|
||||
'editUserGroup' => 1,
|
||||
'userGroup' => $groupName,
|
||||
],
|
||||
''
|
||||
);
|
||||
$userGroupVal['editUsersIcon'] = Generator::getIcon('b_edit', __('Edit'));
|
||||
|
||||
$userGroupVal['deleteUsersUrl'] = Url::getCommon(
|
||||
[
|
||||
'deleteUserGroup' => 1,
|
||||
'userGroup' => $groupName,
|
||||
],
|
||||
''
|
||||
);
|
||||
$userGroupVal['deleteUsersIcon'] = Generator::getIcon('b_drop', __('Delete'));
|
||||
$userGroupsValues[] = $userGroupVal;
|
||||
}
|
||||
}
|
||||
$addUserUrl = Url::getFromRoute('/server/user-groups', ['addUserGroup' => 1]);
|
||||
$addUserIcon = Generator::getIcon('b_usradd');
|
||||
$dbi->freeResult($result);
|
||||
$template = new Template();
|
||||
|
||||
return $template->render('server/user_groups/user_groups', [
|
||||
'action' => $action,
|
||||
'hidden_inputs' => $hidden_inputs ?? '',
|
||||
'result' => $result,
|
||||
'has_rows' => $numRows,
|
||||
'user_groups_values' => $userGroupsValues,
|
||||
'add_user_url' => $addUserUrl,
|
||||
'add_user_icon' => $addUserIcon,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of allowed menu tab names
|
||||
* based on a data row from usergroup table.
|
||||
*
|
||||
* @param array $row row of usergroup table
|
||||
* @param string $level 'server', 'db' or 'table'
|
||||
*
|
||||
* @return string comma separated list of allowed menu tab names
|
||||
*/
|
||||
public static function getAllowedTabNames(array $row, string $level): string
|
||||
{
|
||||
$tabNames = [];
|
||||
$tabs = Util::getMenuTabList($level);
|
||||
foreach ($tabs as $tab => $tabName) {
|
||||
if (isset($row[$level . '_' . $tab])
|
||||
&& $row[$level . '_' . $tab] !== 'Y'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tabNames[] = $tabName;
|
||||
}
|
||||
|
||||
return implode(', ', $tabNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a user group
|
||||
*
|
||||
* @param string $userGroup user group name
|
||||
*/
|
||||
public static function delete(string $userGroup): void
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$relation = new Relation($dbi);
|
||||
$cfgRelation = $relation->getRelationsParam();
|
||||
$userTable = Util::backquote($cfgRelation['db'])
|
||||
. '.' . Util::backquote($cfgRelation['users']);
|
||||
$groupTable = Util::backquote($cfgRelation['db'])
|
||||
. '.' . Util::backquote($cfgRelation['usergroups']);
|
||||
$sql_query = 'DELETE FROM ' . $userTable
|
||||
. " WHERE `usergroup`='" . $dbi->escapeString($userGroup)
|
||||
. "'";
|
||||
$relation->queryAsControlUser($sql_query, true);
|
||||
$sql_query = 'DELETE FROM ' . $groupTable
|
||||
. " WHERE `usergroup`='" . $dbi->escapeString($userGroup)
|
||||
. "'";
|
||||
$relation->queryAsControlUser($sql_query, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for add/edit user group dialog
|
||||
*
|
||||
* @param string $userGroup name of the user group in case of editing
|
||||
*
|
||||
* @return string HTML for add/edit user group dialog
|
||||
*/
|
||||
public static function getHtmlToEditUserGroup(?string $userGroup = null): string
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$relation = new Relation($dbi);
|
||||
$urlParams = [];
|
||||
|
||||
$editUserGroupSpecialChars = '';
|
||||
if ($userGroup !== null) {
|
||||
$editUserGroupSpecialChars = htmlspecialchars($userGroup);
|
||||
}
|
||||
if ($userGroup !== null) {
|
||||
$urlParams['userGroup'] = $userGroup;
|
||||
$urlParams['editUserGroupSubmit'] = '1';
|
||||
} else {
|
||||
$urlParams['addUserGroupSubmit'] = '1';
|
||||
}
|
||||
$allowedTabs = [
|
||||
'server' => [],
|
||||
'db' => [],
|
||||
'table' => [],
|
||||
];
|
||||
if ($userGroup !== null) {
|
||||
$cfgRelation = $relation->getRelationsParam();
|
||||
$groupTable = Util::backquote($cfgRelation['db'])
|
||||
. '.' . Util::backquote($cfgRelation['usergroups']);
|
||||
$sql_query = 'SELECT * FROM ' . $groupTable
|
||||
. " WHERE `usergroup`='" . $dbi->escapeString($userGroup)
|
||||
. "'";
|
||||
$result = $relation->queryAsControlUser($sql_query, false);
|
||||
if ($result) {
|
||||
while ($row = $dbi->fetchAssoc($result)) {
|
||||
$key = $row['tab'];
|
||||
$value = $row['allowed'];
|
||||
if (substr($key, 0, 7) === 'server_' && $value === 'Y') {
|
||||
$allowedTabs['server'][] = mb_substr($key, 7);
|
||||
} elseif (substr($key, 0, 3) === 'db_' && $value === 'Y') {
|
||||
$allowedTabs['db'][] = mb_substr($key, 3);
|
||||
} elseif (substr($key, 0, 6) === 'table_'
|
||||
&& $value === 'Y'
|
||||
) {
|
||||
$allowedTabs['table'][] = mb_substr($key, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
$dbi->freeResult($result);
|
||||
}
|
||||
$tabList = self::getTabList(
|
||||
__('Server-level tabs'),
|
||||
'server',
|
||||
$allowedTabs['server']
|
||||
);
|
||||
$tabList .= self::getTabList(
|
||||
__('Database-level tabs'),
|
||||
'db',
|
||||
$allowedTabs['db']
|
||||
);
|
||||
$tabList .= self::getTabList(
|
||||
__('Table-level tabs'),
|
||||
'table',
|
||||
$allowedTabs['table']
|
||||
);
|
||||
|
||||
$template = new Template();
|
||||
|
||||
return $template->render('server/user_groups/edit_user_groups', [
|
||||
'user_group' => $userGroup,
|
||||
'edit_user_group_special_chars' => $editUserGroupSpecialChars,
|
||||
'user_group_url' => Url::getFromRoute('/server/user-groups'),
|
||||
'hidden_inputs' => Url::getHiddenInputs($urlParams),
|
||||
'tab_list' => $tabList,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for checkbox groups to choose
|
||||
* tabs of 'server', 'db' or 'table' levels.
|
||||
*
|
||||
* @param string $title title of the checkbox group
|
||||
* @param string $level 'server', 'db' or 'table'
|
||||
* @param array $selected array of selected allowed tabs
|
||||
*
|
||||
* @return string HTML for checkbox groups
|
||||
*/
|
||||
public static function getTabList(string $title, string $level, array $selected): string
|
||||
{
|
||||
$tabs = Util::getMenuTabList($level);
|
||||
$tabDetails = [];
|
||||
foreach ($tabs as $tab => $tabName) {
|
||||
$tabDetail = [];
|
||||
$tabDetail['in_array'] = (in_array($tab, $selected) ? ' checked="checked"' : '');
|
||||
$tabDetail['tab'] = $tab;
|
||||
$tabDetail['tab_name'] = $tabName;
|
||||
$tabDetails[] = $tabDetail;
|
||||
}
|
||||
$template = new Template();
|
||||
|
||||
return $template->render('server/user_groups/tab_list', [
|
||||
'title' => $title,
|
||||
'level' => $level,
|
||||
'tab_details' => $tabDetails,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/update a user group with allowed menu tabs.
|
||||
*
|
||||
* @param string $userGroup user group name
|
||||
* @param bool $new whether this is a new user group
|
||||
*/
|
||||
public static function edit(string $userGroup, bool $new = false): void
|
||||
{
|
||||
global $dbi;
|
||||
|
||||
$relation = new Relation($dbi);
|
||||
$tabs = Util::getMenuTabList();
|
||||
$cfgRelation = $relation->getRelationsParam();
|
||||
$groupTable = Util::backquote($cfgRelation['db'])
|
||||
. '.' . Util::backquote($cfgRelation['usergroups']);
|
||||
|
||||
if (! $new) {
|
||||
$sql_query = 'DELETE FROM ' . $groupTable
|
||||
. " WHERE `usergroup`='" . $dbi->escapeString($userGroup)
|
||||
. "';";
|
||||
$relation->queryAsControlUser($sql_query, true);
|
||||
}
|
||||
|
||||
$sql_query = 'INSERT INTO ' . $groupTable
|
||||
. '(`usergroup`, `tab`, `allowed`)'
|
||||
. ' VALUES ';
|
||||
$first = true;
|
||||
foreach ($tabs as $tabGroupName => $tabGroup) {
|
||||
foreach ($tabGroup as $tab => $tabName) {
|
||||
if (! $first) {
|
||||
$sql_query .= ', ';
|
||||
}
|
||||
$tabName = $tabGroupName . '_' . $tab;
|
||||
$allowed = isset($_POST[$tabName]) && $_POST[$tabName] === 'Y';
|
||||
$sql_query .= "('" . $dbi->escapeString($userGroup) . "', '" . $tabName . "', '"
|
||||
. ($allowed ? 'Y' : 'N') . "')";
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
$sql_query .= ';';
|
||||
$relation->queryAsControlUser($sql_query, true);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue