Update website

This commit is contained in:
Guilhem Lavaux 2024-11-19 09:35:33 +01:00
parent bb4b0f9be8
commit 011b183e28
4263 changed files with 3014 additions and 720369 deletions

View file

@ -1,133 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function array_key_exists;
/**
* Handles viewing binary logs
*/
class BinlogController extends AbstractController
{
/**
* binary log files
*
* @var array
*/
protected $binaryLogs;
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
$this->binaryLogs = $this->dbi->fetchResult(
'SHOW MASTER LOGS',
'Log_name'
);
}
public function __invoke(): void
{
global $cfg, $errorUrl;
$params = [
'log' => $_POST['log'] ?? null,
'pos' => $_POST['pos'] ?? null,
'is_full_query' => $_POST['is_full_query'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$position = ! empty($params['pos']) ? (int) $params['pos'] : 0;
$urlParams = [];
if (isset($params['log']) && array_key_exists($params['log'], $this->binaryLogs)) {
$urlParams['log'] = $params['log'];
}
$isFullQuery = false;
if (! empty($params['is_full_query'])) {
$isFullQuery = true;
$urlParams['is_full_query'] = 1;
}
$sqlQuery = $this->getSqlQuery($params['log'] ?? '', $position, (int) $cfg['MaxRows']);
$result = $this->dbi->query($sqlQuery);
$numRows = $result->numRows();
$previousParams = $urlParams;
$fullQueriesParams = $urlParams;
$nextParams = $urlParams;
if ($position > 0) {
$fullQueriesParams['pos'] = $position;
if ($position > $cfg['MaxRows']) {
$previousParams['pos'] = $position - $cfg['MaxRows'];
}
}
$fullQueriesParams['is_full_query'] = 1;
if ($isFullQuery) {
unset($fullQueriesParams['is_full_query']);
}
if ($numRows >= $cfg['MaxRows']) {
$nextParams['pos'] = $position + $cfg['MaxRows'];
}
$values = $result->fetchAllAssoc();
$this->render('server/binlog/index', [
'url_params' => $urlParams,
'binary_logs' => $this->binaryLogs,
'log' => $params['log'],
'sql_message' => Generator::getMessage(Message::success(), $sqlQuery),
'values' => $values,
'has_previous' => $position > 0,
'has_next' => $numRows >= $cfg['MaxRows'],
'previous_params' => $previousParams,
'full_queries_params' => $fullQueriesParams,
'next_params' => $nextParams,
'has_icons' => Util::showIcons('TableNavigationLinksMode'),
'is_full_query' => $isFullQuery,
]);
}
/**
* @param string $log Binary log file name
* @param int $position Position to display
* @param int $maxRows Maximum number of rows
*/
private function getSqlQuery(
string $log,
int $position,
int $maxRows
): string {
$sqlQuery = 'SHOW BINLOG EVENTS';
if (! empty($log)) {
$sqlQuery .= ' IN \'' . $log . '\'';
}
$sqlQuery .= ' LIMIT ' . $position . ', ' . $maxRows;
return $sqlQuery;
}
}

View file

@ -1,80 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\Charsets\Charset;
use PhpMyAdmin\Charsets\Collation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
/**
* Handles viewing character sets and collations
*/
class CollationsController extends AbstractController
{
/** @var array<string, Charset> */
private $charsets;
/** @var array<string, array<string, Collation>> */
private $collations;
/** @var DatabaseInterface */
private $dbi;
/**
* @param array<string, Charset>|null $charsets
* @param array<string, array<string, Collation>>|null $collations
*/
public function __construct(
ResponseRenderer $response,
Template $template,
DatabaseInterface $dbi,
?array $charsets = null,
?array $collations = null
) {
global $cfg;
parent::__construct($response, $template);
$this->dbi = $dbi;
$this->charsets = $charsets ?? Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
$this->collations = $collations ?? Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
}
public function __invoke(): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$charsets = [];
foreach ($this->charsets as $charset) {
$charsetCollations = [];
foreach ($this->collations[$charset->getName()] as $collation) {
$charsetCollations[] = [
'name' => $collation->getName(),
'description' => $collation->getDescription(),
'is_default' => $collation->isDefault(),
];
}
$charsets[] = [
'name' => $charset->getName(),
'description' => $charset->getDescription(),
'collations' => $charsetCollations,
];
}
$this->render('server/collations/index', ['charsets' => $charsets]);
}
}

View file

@ -1,104 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Databases;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function array_key_exists;
use function explode;
use function mb_strlen;
use function mb_strtolower;
use function str_contains;
final class CreateController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $db;
$params = [
'new_db' => $_POST['new_db'] ?? null,
'db_collation' => $_POST['db_collation'] ?? null,
];
if (! isset($params['new_db']) || mb_strlen($params['new_db']) === 0 || ! $this->response->isAjax()) {
$this->response->addJSON(['message' => Message::error()]);
return;
}
// lower_case_table_names=1 `DB` becomes `db`
if ($this->dbi->getLowerCaseNames() === '1') {
$params['new_db'] = mb_strtolower($params['new_db']);
}
/**
* Builds and executes the db creation sql query
*/
$sqlQuery = 'CREATE DATABASE ' . Util::backquote($params['new_db']);
if (! empty($params['db_collation'])) {
[$databaseCharset] = explode('_', $params['db_collation']);
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
$collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
if (
array_key_exists($databaseCharset, $charsets)
&& array_key_exists($params['db_collation'], $collations[$databaseCharset])
) {
$sqlQuery .= ' DEFAULT'
. Util::getCharsetQueryPart($params['db_collation']);
}
}
$sqlQuery .= ';';
$result = $this->dbi->tryQuery($sqlQuery);
if (! $result) {
// avoid displaying the not-created db name in header or navi panel
$db = '';
$message = Message::rawError($this->dbi->getError());
$json = ['message' => $message];
$this->response->setRequestStatus(false);
} else {
$db = $params['new_db'];
$message = Message::success(__('Database %1$s has been created.'));
$message->addParam($params['new_db']);
$scriptName = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$json = [
'message' => $message,
'sql_query' => Generator::getMessage('', $sqlQuery, 'success'),
'url' => $scriptName . Url::getCommon(
['db' => $params['new_db']],
! str_contains($scriptName, '?') ? '?' : '&'
),
];
}
$this->response->addJSON($json);
}
}

View file

@ -1,103 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Databases;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function _ngettext;
use function count;
use function is_array;
final class DestroyController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var Transformations */
private $transformations;
/** @var RelationCleanup */
private $relationCleanup;
public function __construct(
ResponseRenderer $response,
Template $template,
DatabaseInterface $dbi,
Transformations $transformations,
RelationCleanup $relationCleanup
) {
parent::__construct($response, $template);
$this->dbi = $dbi;
$this->transformations = $transformations;
$this->relationCleanup = $relationCleanup;
}
public function __invoke(): void
{
global $selected, $errorUrl, $cfg, $dblist, $reload;
$selected_dbs = $_POST['selected_dbs'] ?? null;
if (
! $this->response->isAjax()
|| (! $this->dbi->isSuperUser() && ! $cfg['AllowUserDropDatabase'])
) {
$message = Message::error();
$json = ['message' => $message];
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON($json);
return;
}
if (
! is_array($selected_dbs)
|| $selected_dbs === []
) {
$message = Message::error(__('No databases selected.'));
$json = ['message' => $message];
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON($json);
return;
}
$errorUrl = Url::getFromRoute('/server/databases');
$selected = $selected_dbs;
$numberOfDatabases = count($selected_dbs);
foreach ($selected_dbs as $database) {
$this->relationCleanup->database($database);
$aQuery = 'DROP DATABASE ' . Util::backquote($database);
$reload = true;
$this->dbi->query($aQuery);
$this->transformations->clear($database);
}
$dblist->databases->build();
$message = Message::success(
_ngettext(
'%1$d database has been dropped successfully.',
'%1$d databases have been dropped successfully.',
$numberOfDatabases
)
);
$message->addParam($numberOfDatabases);
$json = ['message' => $message];
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON($json);
}
}

View file

@ -1,338 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Query\Utilities;
use PhpMyAdmin\ReplicationInfo;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function array_keys;
use function array_search;
use function count;
use function in_array;
use function mb_strtolower;
use function str_contains;
use function strlen;
/**
* Handles viewing and creating and deleting databases
*/
class DatabasesController extends AbstractController
{
/** @var array array of database details */
private $databases = [];
/** @var int number of databases */
private $databaseCount = 0;
/** @var string sort by column */
private $sortBy;
/** @var string sort order of databases */
private $sortOrder;
/** @var bool whether to show database statistics */
private $hasStatistics;
/** @var int position in list navigation */
private $position;
/** @var Transformations */
private $transformations;
/** @var RelationCleanup */
private $relationCleanup;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Transformations $transformations,
RelationCleanup $relationCleanup,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->transformations = $transformations;
$this->relationCleanup = $relationCleanup;
$this->dbi = $dbi;
$checkUserPrivileges = new CheckUserPrivileges($dbi);
$checkUserPrivileges->getPrivileges();
}
public function __invoke(): void
{
global $cfg, $server, $dblist, $is_create_db_priv;
global $db_to_create, $text_dir, $errorUrl;
$params = [
'statistics' => $_REQUEST['statistics'] ?? null,
'pos' => $_REQUEST['pos'] ?? null,
'sort_by' => $_REQUEST['sort_by'] ?? null,
'sort_order' => $_REQUEST['sort_order'] ?? null,
];
$this->addScriptFiles(['server/databases.js']);
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$replicationInfo = new ReplicationInfo($this->dbi);
$replicationInfo->load($_POST['primary_connection'] ?? null);
$primaryInfo = $replicationInfo->getPrimaryInfo();
$replicaInfo = $replicationInfo->getReplicaInfo();
$this->setSortDetails($params['sort_by'], $params['sort_order']);
$this->hasStatistics = ! empty($params['statistics']);
$this->position = ! empty($params['pos']) ? (int) $params['pos'] : 0;
/**
* Gets the databases list
*/
if ($server > 0) {
$this->databases = $this->dbi->getDatabasesFull(
null,
$this->hasStatistics,
DatabaseInterface::CONNECT_USER,
$this->sortBy,
$this->sortOrder,
$this->position,
true
);
$this->databaseCount = count($dblist->databases);
}
$urlParams = [
'statistics' => $this->hasStatistics,
'pos' => $this->position,
'sort_by' => $this->sortBy,
'sort_order' => $this->sortOrder,
];
$databases = $this->getDatabases($primaryInfo, $replicaInfo);
$charsetsList = [];
if ($cfg['ShowCreateDb'] && $is_create_db_priv) {
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
$collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
$serverCollation = $this->dbi->getServerCollation();
foreach ($charsets as $charset) {
$collationsList = [];
foreach ($collations[$charset->getName()] as $collation) {
$collationsList[] = [
'name' => $collation->getName(),
'description' => $collation->getDescription(),
'is_selected' => $serverCollation === $collation->getName(),
];
}
$charsetsList[] = [
'name' => $charset->getName(),
'description' => $charset->getDescription(),
'collations' => $collationsList,
];
}
}
$headerStatistics = $this->getStatisticsColumns();
$this->render('server/databases/index', [
'is_create_database_shown' => $cfg['ShowCreateDb'],
'has_create_database_privileges' => $is_create_db_priv,
'has_statistics' => $this->hasStatistics,
'database_to_create' => $db_to_create,
'databases' => $databases['databases'],
'total_statistics' => $databases['total_statistics'],
'header_statistics' => $headerStatistics,
'charsets' => $charsetsList,
'database_count' => $this->databaseCount,
'pos' => $this->position,
'url_params' => $urlParams,
'max_db_list' => $cfg['MaxDbList'],
'has_primary_replication' => $primaryInfo['status'],
'has_replica_replication' => $replicaInfo['status'],
'is_drop_allowed' => $this->dbi->isSuperUser() || $cfg['AllowUserDropDatabase'],
'text_dir' => $text_dir,
]);
}
/**
* Extracts parameters sort order and sort by
*
* @param string|null $sortBy sort by
* @param string|null $sortOrder sort order
*/
private function setSortDetails(?string $sortBy, ?string $sortOrder): void
{
if (empty($sortBy)) {
$this->sortBy = 'SCHEMA_NAME';
} else {
$sortByAllowList = [
'SCHEMA_NAME',
'DEFAULT_COLLATION_NAME',
'SCHEMA_TABLES',
'SCHEMA_TABLE_ROWS',
'SCHEMA_DATA_LENGTH',
'SCHEMA_INDEX_LENGTH',
'SCHEMA_LENGTH',
'SCHEMA_DATA_FREE',
];
$this->sortBy = 'SCHEMA_NAME';
if (in_array($sortBy, $sortByAllowList)) {
$this->sortBy = $sortBy;
}
}
$this->sortOrder = 'asc';
if (! isset($sortOrder) || mb_strtolower($sortOrder) !== 'desc') {
return;
}
$this->sortOrder = 'desc';
}
/**
* @param array $primaryInfo
* @param array $replicaInfo
*
* @return array
*/
private function getDatabases($primaryInfo, $replicaInfo): array
{
global $cfg;
$databases = [];
$totalStatistics = $this->getStatisticsColumns();
foreach ($this->databases as $database) {
$replication = [
'primary' => ['status' => $primaryInfo['status']],
'replica' => ['status' => $replicaInfo['status']],
];
if ($primaryInfo['status']) {
$key = array_search($database['SCHEMA_NAME'], $primaryInfo['Ignore_DB']);
$replication['primary']['is_replicated'] = false;
if (strlen((string) $key) === 0) {
$key = array_search($database['SCHEMA_NAME'], $primaryInfo['Do_DB']);
if (strlen((string) $key) > 0 || count($primaryInfo['Do_DB']) === 0) {
$replication['primary']['is_replicated'] = true;
}
}
}
if ($replicaInfo['status']) {
$key = array_search($database['SCHEMA_NAME'], $replicaInfo['Ignore_DB']);
$replication['replica']['is_replicated'] = false;
if (strlen((string) $key) === 0) {
$key = array_search($database['SCHEMA_NAME'], $replicaInfo['Do_DB']);
if (strlen((string) $key) > 0 || count($replicaInfo['Do_DB']) === 0) {
$replication['replica']['is_replicated'] = true;
}
}
}
$statistics = $this->getStatisticsColumns();
if ($this->hasStatistics) {
foreach (array_keys($statistics) as $key) {
$statistics[$key]['raw'] = (int) ($database[$key] ?? 0);
$totalStatistics[$key]['raw'] += (int) ($database[$key] ?? 0);
}
}
$url = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$url .= Url::getCommonRaw(
['db' => $database['SCHEMA_NAME']],
! str_contains($url, '?') ? '?' : '&'
);
$databases[$database['SCHEMA_NAME']] = [
'name' => $database['SCHEMA_NAME'],
'collation' => [],
'statistics' => $statistics,
'replication' => $replication,
'is_system_schema' => Utilities::isSystemSchema($database['SCHEMA_NAME'], true),
'is_pmadb' => $database['SCHEMA_NAME'] === ($cfg['Server']['pmadb'] ?? ''),
'url' => $url,
];
$collation = Charsets::findCollationByName(
$this->dbi,
$cfg['Server']['DisableIS'],
$database['DEFAULT_COLLATION_NAME']
);
if ($collation === null) {
continue;
}
$databases[$database['SCHEMA_NAME']]['collation'] = [
'name' => $collation->getName(),
'description' => $collation->getDescription(),
];
}
return [
'databases' => $databases,
'total_statistics' => $totalStatistics,
];
}
/**
* Prepares the statistics columns
*
* @return array
*/
private function getStatisticsColumns(): array
{
return [
'SCHEMA_TABLES' => [
'title' => __('Tables'),
'format' => 'number',
'raw' => 0,
],
'SCHEMA_TABLE_ROWS' => [
'title' => __('Rows'),
'format' => 'number',
'raw' => 0,
],
'SCHEMA_DATA_LENGTH' => [
'title' => __('Data'),
'format' => 'byte',
'raw' => 0,
],
'SCHEMA_INDEX_LENGTH' => [
'title' => __('Indexes'),
'format' => 'byte',
'raw' => 0,
],
'SCHEMA_LENGTH' => [
'title' => __('Total'),
'format' => 'byte',
'raw' => 0,
],
'SCHEMA_DATA_FREE' => [
'title' => __('Overhead'),
'format' => 'byte',
'raw' => 0,
],
];
}
}

View file

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\StorageEngine;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
/**
* Handles viewing storage engine details
*/
class EnginesController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->render('server/engines/index', [
'engines' => StorageEngine::getStorageEngines(),
]);
}
}

View file

@ -1,95 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Export\Options;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function __;
use function array_merge;
final class ExportController extends AbstractController
{
/** @var Options */
private $export;
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, Options $export, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->export = $export;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $table, $sql_query, $num_tables, $unlim_num_rows;
global $tmp_select, $select_item, $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$pageSettings = new PageSettings('Export');
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
$pageSettingsHtml = $pageSettings->getHTML();
$this->addScriptFiles(['export.js']);
$select_item = $tmp_select ?? '';
$databases = $this->export->getDatabasesForSelectOptions($select_item);
if (! isset($sql_query)) {
$sql_query = '';
}
if (! isset($num_tables)) {
$num_tables = 0;
}
if (! isset($unlim_num_rows)) {
$unlim_num_rows = 0;
}
$GLOBALS['single_table'] = $_POST['single_table'] ?? $_GET['single_table'] ?? $GLOBALS['single_table'] ?? null;
$exportList = Plugins::getExport('server', isset($GLOBALS['single_table']));
if (empty($exportList)) {
$this->response->addHTML(Message::error(
__('Could not load export plugins, please check your installation!')
)->getDisplay());
return;
}
$options = $this->export->getOptions(
'server',
$db,
$table,
$sql_query,
$num_tables,
$unlim_num_rows,
$exportList
);
$this->render('server/export/index', array_merge($options, [
'page_settings_error_html' => $pageSettingsErrorHtml,
'page_settings_html' => $pageSettingsHtml,
'databases' => $databases,
]));
}
}

View file

@ -1,119 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Encoding;
use PhpMyAdmin\Import;
use PhpMyAdmin\Import\Ajax;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use PhpMyAdmin\Utils\ForeignKey;
use function __;
use function intval;
use function is_numeric;
final class ImportController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $table, $SESSION_KEY, $cfg, $errorUrl;
$pageSettings = new PageSettings('Import');
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
$pageSettingsHtml = $pageSettings->getHTML();
$this->addScriptFiles(['import.js']);
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
[$SESSION_KEY, $uploadId] = Ajax::uploadProgressSetup();
$importList = Plugins::getImport('server');
if (empty($importList)) {
$this->response->addHTML(Message::error(__(
'Could not load import plugins, please check your installation!'
))->getDisplay());
return;
}
$offset = null;
if (isset($_REQUEST['offset']) && is_numeric($_REQUEST['offset'])) {
$offset = intval($_REQUEST['offset']);
}
$timeoutPassed = $_REQUEST['timeout_passed'] ?? null;
$localImportFile = $_REQUEST['local_import_file'] ?? null;
$compressions = Import::getCompressions();
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
$idKey = $_SESSION[$SESSION_KEY]['handler']::getIdKey();
$hiddenInputs = [
$idKey => $uploadId,
'import_type' => 'server',
];
$default = isset($_GET['format']) ? (string) $_GET['format'] : Plugins::getDefault('Import', 'format');
$choice = Plugins::getChoice($importList, $default);
$options = Plugins::getOptions('Import', $importList);
$skipQueriesDefault = Plugins::getDefault('Import', 'skip_queries');
$isAllowInterruptChecked = Plugins::checkboxCheck('Import', 'allow_interrupt');
$maxUploadSize = (int) $GLOBALS['config']->get('max_upload_size');
$this->render('server/import/index', [
'page_settings_error_html' => $pageSettingsErrorHtml,
'page_settings_html' => $pageSettingsHtml,
'upload_id' => $uploadId,
'handler' => $_SESSION[$SESSION_KEY]['handler'],
'hidden_inputs' => $hiddenInputs,
'db' => $db,
'table' => $table,
'max_upload_size' => $maxUploadSize,
'formatted_maximum_upload_size' => Util::getFormattedMaximumUploadSize($maxUploadSize),
'plugins_choice' => $choice,
'options' => $options,
'skip_queries_default' => $skipQueriesDefault,
'is_allow_interrupt_checked' => $isAllowInterruptChecked,
'local_import_file' => $localImportFile,
'is_upload' => $GLOBALS['config']->get('enable_upload'),
'upload_dir' => $cfg['UploadDir'] ?? null,
'timeout_passed_global' => $GLOBALS['timeout_passed'] ?? null,
'compressions' => $compressions,
'is_encoding_supported' => Encoding::isSupported(),
'encodings' => Encoding::listEncodings(),
'import_charset' => $cfg['Import']['charset'] ?? null,
'timeout_passed' => $timeoutPassed,
'offset' => $offset,
'can_convert_kanji' => Encoding::canConvertKanji(),
'charsets' => $charsets,
'is_foreign_key_check' => ForeignKey::isCheckEnabled(),
'user_upload_dir' => Util::userDir((string) ($cfg['UploadDir'] ?? '')),
'local_files' => Import::getLocalFiles($importList),
]);
}
}

View file

@ -1,75 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Plugins;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function array_keys;
use function ksort;
use function mb_strtolower;
use function preg_replace;
/**
* Handles viewing server plugin details
*/
class PluginsController extends AbstractController
{
/** @var Plugins */
private $plugins;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Plugins $plugins,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->plugins = $plugins;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->addScriptFiles(['vendor/jquery/jquery.tablesorter.js', 'server/plugins.js']);
$plugins = [];
$serverPlugins = $this->plugins->getAll();
foreach ($serverPlugins as $plugin) {
$plugins[$plugin->getType()][] = $plugin->toArray();
}
ksort($plugins);
$cleanTypes = [];
foreach (array_keys($plugins) as $type) {
$cleanTypes[$type] = preg_replace(
'/[^a-z]/',
'',
mb_strtolower($type)
);
}
$this->render('server/plugins/index', [
'plugins' => $plugins,
'clean_types' => $cleanTypes,
]);
}
}

View file

@ -1,52 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Privileges;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Privileges\AccountLocking;
use PhpMyAdmin\Template;
use Throwable;
use function __;
final class AccountLockController extends AbstractController
{
/** @var AccountLocking */
private $model;
public function __construct(ResponseRenderer $response, Template $template, AccountLocking $accountLocking)
{
parent::__construct($response, $template);
$this->model = $accountLocking;
}
public function __invoke(ServerRequest $request): void
{
$this->response->setAjax(true);
/** @var string $userName */
$userName = $request->getParsedBodyParam('username');
/** @var string $hostName */
$hostName = $request->getParsedBodyParam('hostname');
try {
$this->model->lock($userName, $hostName);
} catch (Throwable $exception) {
$this->response->setHttpResponseCode(400);
$this->response->setRequestStatus(false);
$this->response->addJSON(['message' => Message::error($exception->getMessage())]);
return;
}
$message = Message::success(__('The account %s@%s has been successfully locked.'));
$message->addParam($userName);
$message->addParam($hostName);
$this->response->addJSON(['message' => $message]);
}
}

View file

@ -1,52 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Privileges;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Privileges\AccountLocking;
use PhpMyAdmin\Template;
use Throwable;
use function __;
final class AccountUnlockController extends AbstractController
{
/** @var AccountLocking */
private $model;
public function __construct(ResponseRenderer $response, Template $template, AccountLocking $accountLocking)
{
parent::__construct($response, $template);
$this->model = $accountLocking;
}
public function __invoke(ServerRequest $request): void
{
$this->response->setAjax(true);
/** @var string $userName */
$userName = $request->getParsedBodyParam('username');
/** @var string $hostName */
$hostName = $request->getParsedBodyParam('hostname');
try {
$this->model->unlock($userName, $hostName);
} catch (Throwable $exception) {
$this->response->setHttpResponseCode(400);
$this->response->setRequestStatus(false);
$this->response->addJSON(['message' => Message::error($exception->getMessage())]);
return;
}
$message = Message::success(__('The account %s@%s has been successfully unlocked.'));
$message->addParam($userName);
$message->addParam($hostName);
$this->response->addJSON(['message' => $message]);
}
}

View file

@ -1,475 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Controllers\Database\PrivilegesController as DatabaseController;
use PhpMyAdmin\Controllers\Table\PrivilegesController as TableController;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Plugins;
use PhpMyAdmin\Server\Privileges;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function header;
use function implode;
use function is_array;
use function is_string;
use function ob_get_clean;
use function ob_start;
use function str_replace;
use function urlencode;
/**
* Server privileges and users manipulations.
*/
class PrivilegesController extends AbstractController
{
/** @var Relation */
private $relation;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Relation $relation,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->relation = $relation;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $table, $errorUrl, $message, $text_dir, $post_patterns;
global $username, $hostname, $dbname, $tablename, $routinename, $db_and_table, $dbname_is_wildcard;
global $queries, $password, $ret_message, $ret_queries, $queries_for_display, $sql_query, $_add_user_error;
global $itemType, $tables, $num_tables, $total_num_tables, $sub_part;
global $tooltip_truename, $tooltip_aliasname, $pos, $title, $export, $grants, $one_grant, $url_dbname;
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
$checkUserPrivileges->getPrivileges();
$relationParameters = $this->relation->getRelationParameters();
$this->addScriptFiles(['server/privileges.js', 'vendor/zxcvbn-ts.js']);
$relationCleanup = new RelationCleanup($this->dbi, $this->relation);
$serverPrivileges = new Privileges(
$this->template,
$this->dbi,
$this->relation,
$relationCleanup,
new Plugins($this->dbi)
);
$databaseController = new DatabaseController(
$this->response,
$this->template,
$db,
$serverPrivileges,
$this->dbi
);
$tableController = new TableController(
$this->response,
$this->template,
$db,
$table,
$serverPrivileges,
$this->dbi
);
if (
(isset($_GET['viewing_mode'])
&& $_GET['viewing_mode'] === 'server')
&& $relationParameters->configurableMenusFeature !== null
) {
$this->response->addHTML('<div class="container-fluid">');
$this->render('server/privileges/subnav', [
'active' => 'privileges',
'is_super_user' => $this->dbi->isSuperUser(),
]);
}
/**
* Sets globals from $_POST patterns, for privileges and max_* vars
*/
$post_patterns = [
'/_priv$/i',
'/^max_/i',
];
Core::setPostAsGlobal($post_patterns);
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$_add_user_error = false;
/**
* Get DB information: username, hostname, dbname,
* tablename, db_and_table, dbname_is_wildcard
*/
[
$username,
$hostname,
$dbname,
$tablename,
$routinename,
$db_and_table,
$dbname_is_wildcard,
] = $serverPrivileges->getDataForDBInfo();
/**
* Checks if the user is allowed to do what they try to...
*/
$isGrantUser = $this->dbi->isGrantUser();
$isCreateUser = $this->dbi->isCreateUser();
if (! $this->dbi->isSuperUser() && ! $isGrantUser && ! $isCreateUser) {
$this->render('server/sub_page_header', [
'type' => 'privileges',
'is_image' => false,
]);
$this->response->addHTML(
Message::error(__('No Privileges'))
->getDisplay()
);
return;
}
if (! $isGrantUser && ! $isCreateUser) {
$this->response->addHTML(Message::notice(
__('You do not have the privileges to administrate the users!')
)->getDisplay());
}
/**
* Checks if the user is using "Change Login Information / Copy User" dialog
* only to update the password
*/
if (
isset($_POST['change_copy']) && $username == $_POST['old_username']
&& $hostname == $_POST['old_hostname']
) {
$this->response->addHTML(
Message::error(
__(
"Username and hostname didn't change. "
. 'If you only want to change the password, '
. "'Change password' tab should be used."
)
)->getDisplay()
);
$this->response->setRequestStatus(false);
return;
}
/**
* Changes / copies a user, part I
*/
[$queries, $password] = $serverPrivileges->getDataForChangeOrCopyUser();
/**
* Adds a user
* (Changes / copies a user, part II)
*/
[
$ret_message,
$ret_queries,
$queries_for_display,
$sql_query,
$_add_user_error,
] = $serverPrivileges->addUser(
$dbname ?? null,
$username ?? '',
$hostname ?? '',
$password ?? null,
$relationParameters->configurableMenusFeature !== null
);
//update the old variables
if (isset($ret_queries)) {
$queries = $ret_queries;
unset($ret_queries);
}
if (isset($ret_message)) {
$message = $ret_message;
unset($ret_message);
}
/**
* Changes / copies a user, part III
*/
if (isset($_POST['change_copy']) && $username !== null && $hostname !== null) {
$queries = $serverPrivileges->getDbSpecificPrivsQueriesForChangeOrCopyUser($queries, $username, $hostname);
}
$itemType = '';
if (! empty($routinename) && is_string($dbname)) {
$itemType = $serverPrivileges->getRoutineType($dbname, $routinename);
}
/**
* Updates privileges
*/
if (! empty($_POST['update_privs'])) {
if (is_array($dbname)) {
foreach ($dbname as $key => $db_name) {
[$sql_query[$key], $message] = $serverPrivileges->updatePrivileges(
($username ?? ''),
($hostname ?? ''),
($tablename ?? ($routinename ?? '')),
($db_name ?? ''),
$itemType
);
}
$sql_query = implode("\n", $sql_query);
} else {
[$sql_query, $message] = $serverPrivileges->updatePrivileges(
($username ?? ''),
($hostname ?? ''),
($tablename ?? ($routinename ?? '')),
($dbname ?? ''),
$itemType
);
}
}
/**
* Assign users to user groups
*/
if (
! empty($_POST['changeUserGroup']) && $relationParameters->configurableMenusFeature !== null
&& $this->dbi->isSuperUser() && $this->dbi->isCreateUser()
) {
$serverPrivileges->setUserGroup($username ?? '', $_POST['userGroup']);
$message = Message::success();
}
/**
* Revokes Privileges
*/
if (isset($_POST['revokeall'])) {
[$message, $sql_query] = $serverPrivileges->getMessageAndSqlQueryForPrivilegesRevoke(
(is_string($dbname) ? $dbname : ''),
($tablename ?? ($routinename ?? '')),
$username ?? '',
$hostname ?? '',
$itemType
);
}
/**
* Updates the password
*/
if (isset($_POST['change_pw'])) {
$message = $serverPrivileges->updatePassword($errorUrl, $username ?? '', $hostname ?? '');
}
/**
* Deletes users
* (Changes / copies a user, part IV)
*/
if (isset($_POST['delete']) || (isset($_POST['change_copy']) && $_POST['mode'] < 4)) {
$queries = $serverPrivileges->getDataForDeleteUsers($queries);
if (empty($_POST['change_copy'])) {
[$sql_query, $message] = $serverPrivileges->deleteUser($queries);
}
}
/**
* Changes / copies a user, part V
*/
if (isset($_POST['change_copy'])) {
$queries = $serverPrivileges->getDataForQueries($queries, $queries_for_display);
$message = Message::success();
$sql_query = implode("\n", $queries);
}
/**
* Reloads the privilege tables into memory
*/
$message_ret = $serverPrivileges->updateMessageForReload();
if ($message_ret !== null) {
$message = $message_ret;
unset($message_ret);
}
/**
* If we are in an Ajax request for Create User/Edit User/Revoke User/
* Flush Privileges, show $message and return.
*/
if (
$this->response->isAjax()
&& empty($_REQUEST['ajax_page_request'])
&& ! isset($_GET['export'])
&& (! isset($_POST['submit_mult']) || $_POST['submit_mult'] !== 'export')
&& ((! isset($_GET['initial']) || $_GET['initial'] === '')
|| (isset($_POST['delete']) && $_POST['delete'] === __('Go')))
&& ! isset($_GET['showall'])
) {
$extra_data = $serverPrivileges->getExtraDataForAjaxBehavior(
($password ?? ''),
($sql_query ?? ''),
($hostname ?? ''),
($username ?? '')
);
if (! empty($message) && $message instanceof Message) {
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
$this->response->addJSON($extra_data);
return;
}
}
/**
* Displays the links
*/
if (isset($_GET['viewing_mode']) && $_GET['viewing_mode'] === 'db') {
$db = $_REQUEST['db'] = $_GET['checkprivsdb'];
// Gets the database structure
$sub_part = '_structure';
ob_start();
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part);
$content = ob_get_clean();
$this->response->addHTML($content . "\n");
} elseif (! empty($GLOBALS['message'])) {
$this->response->addHTML(Generator::getMessage($GLOBALS['message']));
unset($GLOBALS['message']);
}
// export user definition
if (isset($_GET['export']) || (isset($_POST['submit_mult']) && $_POST['submit_mult'] === 'export')) {
[$title, $export] = $serverPrivileges->getListForExportUserDefinition($username ?? '', $hostname ?? '');
unset($username, $hostname, $grants, $one_grant);
if ($this->response->isAjax()) {
$this->response->addJSON('message', $export);
$this->response->addJSON('title', $title);
return;
}
$this->response->addHTML('<h2>' . $title . '</h2>' . $export);
}
// Show back the form if an error occurred
if (isset($_GET['adduser']) || $_add_user_error === true) {
// Add user
$this->response->addHTML(
$serverPrivileges->getHtmlForAddUser(Util::escapeMysqlWildcards(is_string($dbname) ? $dbname : ''))
);
} elseif (isset($_GET['checkprivsdb']) && is_string($_GET['checkprivsdb'])) {
if (isset($_GET['checkprivstable']) && is_string($_GET['checkprivstable'])) {
$this->response->addHTML($tableController([
'checkprivsdb' => $_GET['checkprivsdb'],
'checkprivstable' => $_GET['checkprivstable'],
]));
$this->render('export_modal');
} elseif ($this->response->isAjax() === true && empty($_REQUEST['ajax_page_request'])) {
$message = Message::success(__('User has been added.'));
$this->response->addJSON('message', $message);
return;
} else {
$this->response->addHTML($databaseController(['checkprivsdb' => $_GET['checkprivsdb']]));
$this->render('export_modal');
}
} else {
if (isset($dbname) && ! is_array($dbname)) {
$url_dbname = urlencode(
str_replace(
[
'\_',
'\%',
],
[
'_',
'%',
],
$dbname
)
);
}
if (! isset($username)) {
// No username is given --> display the overview
$this->response->addHTML(
$serverPrivileges->getHtmlForUserOverview($text_dir)
);
} elseif (! empty($routinename)) {
$this->response->addHTML(
$serverPrivileges->getHtmlForRoutineSpecificPrivileges(
$username,
$hostname ?? '',
is_string($dbname) ? $dbname : '',
$routinename,
Util::escapeMysqlWildcards($url_dbname ?? '')
)
);
} else {
// A user was selected -> display the user's properties
// In an Ajax request, prevent cached values from showing
if ($this->response->isAjax()) {
header('Cache-Control: no-cache');
}
$this->response->addHTML(
$serverPrivileges->getHtmlForUserProperties(
$dbname_is_wildcard,
Util::escapeMysqlWildcards($url_dbname ?? ''),
$username,
$hostname ?? '',
$dbname ?? '',
$tablename ?? ''
)
);
}
}
if (
! isset($_GET['viewing_mode'])
|| $_GET['viewing_mode'] !== 'server'
|| $relationParameters->configurableMenusFeature === null
) {
return;
}
$this->response->addHTML('</div>');
}
}

View file

@ -1,109 +0,0 @@
<?php
/**
* Server replications
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ReplicationGui;
use PhpMyAdmin\ReplicationInfo;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function is_array;
/**
* Server replications
*/
class ReplicationController extends AbstractController
{
/** @var ReplicationGui */
private $replicationGui;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
ReplicationGui $replicationGui,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->replicationGui = $replicationGui;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $urlParams, $errorUrl;
$params = [
'url_params' => $_POST['url_params'] ?? null,
'primary_configure' => $_POST['primary_configure'] ?? null,
'replica_configure' => $_POST['replica_configure'] ?? null,
'repl_clear_scr' => $_POST['repl_clear_scr'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$replicationInfo = new ReplicationInfo($this->dbi);
$replicationInfo->load($_POST['primary_connection'] ?? null);
$primaryInfo = $replicationInfo->getPrimaryInfo();
$replicaInfo = $replicationInfo->getReplicaInfo();
$this->addScriptFiles(['server/privileges.js', 'replication.js', 'vendor/zxcvbn-ts.js']);
if (isset($params['url_params']) && is_array($params['url_params'])) {
$urlParams = $params['url_params'];
}
if ($this->dbi->isSuperUser()) {
$this->replicationGui->handleControlRequest();
}
$errorMessages = $this->replicationGui->getHtmlForErrorMessage();
if ($primaryInfo['status']) {
$primaryReplicationHtml = $this->replicationGui->getHtmlForPrimaryReplication();
}
if (isset($params['primary_configure'])) {
$primaryConfigurationHtml = $this->replicationGui->getHtmlForPrimaryConfiguration();
} else {
if (! isset($params['repl_clear_scr'])) {
$replicaConfigurationHtml = $this->replicationGui->getHtmlForReplicaConfiguration(
$replicaInfo['status'],
$replicationInfo->getReplicaStatus()
);
}
if (isset($params['replica_configure'])) {
$changePrimaryHtml = $this->replicationGui->getHtmlForReplicationChangePrimary('replica_changeprimary');
}
}
$this->render('server/replication/index', [
'url_params' => $urlParams,
'is_super_user' => $this->dbi->isSuperUser(),
'error_messages' => $errorMessages,
'is_primary' => $primaryInfo['status'],
'primary_configure' => $params['primary_configure'],
'replica_configure' => $params['replica_configure'],
'clear_screen' => $params['repl_clear_scr'],
'primary_replication_html' => $primaryReplicationHtml ?? '',
'primary_configuration_html' => $primaryConfigurationHtml ?? '',
'replica_configuration_html' => $replicaConfigurationHtml ?? '',
'change_primary_html' => $changePrimaryHtml ?? '',
]);
}
}

View file

@ -1,65 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\StorageEngine;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
/**
* Displays details about a given Storage Engine.
*/
final class ShowEngineController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
/**
* @param array $params
* @psalm-param array{engine: string, page?: string} $params
*/
public function __invoke(ServerRequest $request, array $params): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$page = $params['page'] ?? '';
$engine = [];
if (StorageEngine::isValid($params['engine'])) {
$storageEngine = StorageEngine::getEngine($params['engine']);
$engine = [
'engine' => $params['engine'],
'title' => $storageEngine->getTitle(),
'help_page' => $storageEngine->getMysqlHelpPage(),
'comment' => $storageEngine->getComment(),
'info_pages' => $storageEngine->getInfoPages(),
'support' => $storageEngine->getSupportInformationMessage(),
'variables' => $storageEngine->getHtmlVariables(),
'page' => ! empty($page) ? $storageEngine->getPage($page) : '',
];
}
$this->render('server/engines/show', [
'engine' => $engine,
'page' => $page,
]);
}
}

View file

@ -1,54 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\SqlQueryForm;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
/**
* Server SQL executor
*/
class SqlController extends AbstractController
{
/** @var SqlQueryForm */
private $sqlQueryForm;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
SqlQueryForm $sqlQueryForm,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->sqlQueryForm = $sqlQueryForm;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$this->addScriptFiles(['makegrid.js', 'vendor/jquery/jquery.uitablefilter.js', 'sql.js']);
$pageSettings = new PageSettings('Sql');
$this->response->addHTML($pageSettings->getErrorHTML());
$this->response->addHTML($pageSettings->getHTML());
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->response->addHTML($this->sqlQueryForm->getHtml('', ''));
}
}

View file

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\Controllers\AbstractController as Controller;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Template;
abstract class AbstractController extends Controller
{
/** @var Data */
protected $data;
public function __construct(ResponseRenderer $response, Template $template, Data $data)
{
parent::__construct($response, $template);
$this->data = $data;
}
}

View file

@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\Advisor;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Template;
/**
* Displays the advisor feature
*/
class AdvisorController extends AbstractController
{
/** @var Advisor */
private $advisor;
public function __construct(ResponseRenderer $response, Template $template, Data $data, Advisor $advisor)
{
parent::__construct($response, $template, $data);
$this->advisor = $advisor;
}
public function __invoke(): void
{
$data = [];
if ($this->data->dataLoaded) {
$data = $this->advisor->run();
}
$this->render('server/status/advisor/index', ['data' => $data]);
}
}

View file

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Monitor;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class ChartingDataController extends AbstractController
{
/** @var Monitor */
private $monitor;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
Monitor $monitor,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $data);
$this->monitor = $monitor;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = ['requiredData' => $_POST['requiredData'] ?? null];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
if (! $this->response->isAjax()) {
return;
}
$this->response->addJSON([
'message' => $this->monitor->getJsonForChartingData(
$params['requiredData'] ?? ''
),
]);
}
}

View file

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Monitor;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class GeneralLogController extends AbstractController
{
/** @var Monitor */
private $monitor;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
Monitor $monitor,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $data);
$this->monitor = $monitor;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = [
'time_start' => $_POST['time_start'] ?? null,
'time_end' => $_POST['time_end'] ?? null,
'limitTypes' => $_POST['limitTypes'] ?? null,
'removeVariables' => $_POST['removeVariables'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
if (! $this->response->isAjax()) {
return;
}
$data = $this->monitor->getJsonForLogDataTypeGeneral(
(int) $params['time_start'],
(int) $params['time_end'],
(bool) $params['limitTypes'],
(bool) $params['removeVariables']
);
if ($data === null) {
$this->response->setRequestStatus(false);
return;
}
$this->response->addJSON(['message' => $data]);
}
}

View file

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Monitor;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class LogVarsController extends AbstractController
{
/** @var Monitor */
private $monitor;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
Monitor $monitor,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $data);
$this->monitor = $monitor;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = [
'varName' => $_POST['varName'] ?? null,
'varValue' => $_POST['varValue'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
if (! $this->response->isAjax()) {
return;
}
$this->response->addJSON([
'message' => $this->monitor->getJsonForLoggingVars(
$params['varName'],
$params['varValue']
),
]);
}
}

View file

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Monitor;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class QueryAnalyzerController extends AbstractController
{
/** @var Monitor */
private $monitor;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
Monitor $monitor,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $data);
$this->monitor = $monitor;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = [
'database' => $_POST['database'] ?? null,
'query' => $_POST['query'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
if (! $this->response->isAjax()) {
return;
}
$this->response->addJSON([
'message' => $this->monitor->getJsonForQueryAnalyzer(
$params['database'] ?? '',
$params['query'] ?? ''
),
]);
}
}

View file

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Monitor;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class SlowLogController extends AbstractController
{
/** @var Monitor */
private $monitor;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
Monitor $monitor,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $data);
$this->monitor = $monitor;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = [
'time_start' => $_POST['time_start'] ?? null,
'time_end' => $_POST['time_end'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
if (! $this->response->isAjax()) {
return;
}
$data = $this->monitor->getJsonForLogDataTypeSlow(
(int) $params['time_start'],
(int) $params['time_end']
);
if ($data === null) {
$this->response->setRequestStatus(false);
return;
}
$this->response->addJSON(['message' => $data]);
}
}

View file

@ -1,76 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\SysInfo\SysInfo;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function is_numeric;
use function microtime;
class MonitorController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $data);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->addScriptFiles([
'vendor/jquery/jquery.tablesorter.js',
'jquery.sortable-table.js',
'vendor/jqplot/jquery.jqplot.js',
'vendor/jqplot/plugins/jqplot.pieRenderer.js',
'vendor/jqplot/plugins/jqplot.enhancedPieLegendRenderer.js',
'vendor/jqplot/plugins/jqplot.canvasTextRenderer.js',
'vendor/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
'vendor/jqplot/plugins/jqplot.dateAxisRenderer.js',
'vendor/jqplot/plugins/jqplot.highlighter.js',
'vendor/jqplot/plugins/jqplot.cursor.js',
'jqplot/plugins/jqplot.byteFormatter.js',
'server/status/monitor.js',
'server/status/sorter.js',
'chart.js',// Needed by createProfilingChart in server/status/monitor.js
]);
$form = [
'server_time' => (int) (microtime(true) * 1000),
'server_os' => SysInfo::getOs(),
'is_superuser' => $this->dbi->isSuperUser(),
'server_db_isLocal' => $this->data->dbIsLocal,
];
$javascriptVariableNames = [];
foreach ($this->data->status as $name => $value) {
if (! is_numeric($value)) {
continue;
}
$javascriptVariableNames[] = $name;
}
$this->render('server/status/monitor/index', [
'javascript_variable_names' => $javascriptVariableNames,
'form' => $form,
]);
}
}

View file

@ -1,58 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Processes;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Template;
use function __;
final class KillController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $data);
$this->dbi = $dbi;
}
/**
* @param array $params Request parameters
*/
public function __invoke(ServerRequest $request, array $params): void
{
if (! $this->response->isAjax()) {
return;
}
$kill = (int) $params['id'];
$query = $this->dbi->getKillQuery($kill);
if ($this->dbi->tryQuery($query)) {
$message = Message::success(
__('Thread %s was successfully killed.')
);
$this->response->setRequestStatus(true);
} else {
$message = Message::error(
__(
'phpMyAdmin was unable to kill thread %s. It probably has already been closed.'
)
);
$this->response->setRequestStatus(false);
}
$message->addParam($kill);
$this->response->addJSON(['message' => $message]);
}
}

View file

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status\Processes;
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Processes;
use PhpMyAdmin\Template;
final class RefreshController extends AbstractController
{
/** @var Processes */
private $processes;
public function __construct(ResponseRenderer $response, Template $template, Data $data, Processes $processes)
{
parent::__construct($response, $template, $data);
$this->processes = $processes;
}
public function __invoke(): void
{
$params = [
'showExecuting' => $_POST['showExecuting'] ?? null,
'full' => $_POST['full'] ?? null,
'column_name' => $_POST['column_name'] ?? null,
'order_by_field' => $_POST['order_by_field'] ?? null,
'sort_order' => $_POST['sort_order'] ?? null,
];
if (! $this->response->isAjax()) {
return;
}
$this->render('server/status/processes/list', $this->processes->getList($params));
}
}

View file

@ -1,74 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Server\Status\Processes;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
class ProcessesController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var Processes */
private $processes;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
DatabaseInterface $dbi,
Processes $processes
) {
parent::__construct($response, $template, $data);
$this->dbi = $dbi;
$this->processes = $processes;
}
public function __invoke(): void
{
global $errorUrl;
$params = [
'showExecuting' => $_POST['showExecuting'] ?? null,
'full' => $_POST['full'] ?? null,
'column_name' => $_POST['column_name'] ?? null,
'order_by_field' => $_POST['order_by_field'] ?? null,
'sort_order' => $_POST['sort_order'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->addScriptFiles(['server/status/processes.js']);
$isChecked = false;
if (! empty($params['showExecuting'])) {
$isChecked = true;
}
$urlParams = [
'ajax_request' => true,
'full' => $params['full'] ?? '',
'column_name' => $params['column_name'] ?? '',
'order_by_field' => $params['order_by_field'] ?? '',
'sort_order' => $params['sort_order'] ?? '',
];
$listHtml = $this->template->render('server/status/processes/list', $this->processes->getList($params));
$this->render('server/status/processes/index', [
'url_params' => $urlParams,
'is_checked' => $isChecked,
'server_process_list' => $listHtml,
]);
}
}

View file

@ -1,106 +0,0 @@
<?php
/**
* Displays query statistics for the server
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function __;
use function array_sum;
use function arsort;
use function count;
use function str_replace;
class QueriesController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $data);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->addScriptFiles([
'chart.js',
'vendor/jqplot/jquery.jqplot.js',
'vendor/jqplot/plugins/jqplot.pieRenderer.js',
'vendor/jqplot/plugins/jqplot.highlighter.js',
'vendor/jqplot/plugins/jqplot.enhancedPieLegendRenderer.js',
'vendor/jquery/jquery.tablesorter.js',
'server/status/sorter.js',
'server/status/queries.js',
]);
if ($this->data->dataLoaded) {
$hourFactor = 3600 / $this->data->status['Uptime'];
$usedQueries = $this->data->usedQueries;
$totalQueries = array_sum($usedQueries);
$stats = [
'total' => $totalQueries,
'per_hour' => $totalQueries * $hourFactor,
'per_minute' => $totalQueries * 60 / $this->data->status['Uptime'],
'per_second' => $totalQueries / $this->data->status['Uptime'],
];
// reverse sort by value to show most used statements first
arsort($usedQueries);
$chart = [];
$querySum = array_sum($usedQueries);
$otherSum = 0;
$queries = [];
foreach ($usedQueries as $key => $value) {
// For the percentage column, use Questions - Connections, because
// the number of connections is not an item of the Query types
// but is included in Questions. Then the total of the percentages is 100.
$name = str_replace(['Com_', '_'], ['', ' '], $key);
// Group together values that make out less than 2% into "Other", but only
// if we have more than 6 fractions already
if ($value < $querySum * 0.02 && count($chart) > 6) {
$otherSum += $value;
} else {
$chart[$name] = $value;
}
$queries[$key] = [
'name' => $name,
'value' => $value,
'per_hour' => $value * $hourFactor,
'percentage' => $value * 100 / $totalQueries,
];
}
if ($otherSum > 0) {
$chart[__('Other')] = $otherSum;
}
}
$this->render('server/status/queries/index', [
'is_data_loaded' => $this->data->dataLoaded,
'stats' => $stats ?? null,
'queries' => $queries ?? [],
'chart' => $chart ?? [],
]);
}
}

View file

@ -1,206 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ReplicationGui;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function implode;
/**
* Object the server status page: processes, connections and traffic.
*/
class StatusController extends AbstractController
{
/** @var ReplicationGui */
private $replicationGui;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Data $data,
ReplicationGui $replicationGui,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $data);
$this->replicationGui = $replicationGui;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$replicationInfo = $this->data->getReplicationInfo();
$primaryInfo = $replicationInfo->getPrimaryInfo();
$replicaInfo = $replicationInfo->getReplicaInfo();
$traffic = [];
$connections = [];
$replication = '';
if ($this->data->dataLoaded) {
// In some case the data was reported not to exist, check it for all keys
if (isset($this->data->status['Bytes_received'], $this->data->status['Bytes_sent'])) {
/** @var string[] $bytes */
$bytes = Util::formatByteDown(
$this->data->status['Bytes_received'] + $this->data->status['Bytes_sent'],
3,
1
);
$networkTraffic = implode(' ', $bytes);
}
if (isset($this->data->status['Uptime'])) {
$uptime = Util::timespanFormat($this->data->status['Uptime']);
}
$startTime = Util::localisedDate($this->getStartTime());
$traffic = $this->getTrafficInfo();
$connections = $this->getConnectionsInfo();
if ($primaryInfo['status']) {
$replication .= $this->replicationGui->getHtmlForReplicationStatusTable('primary');
}
if ($replicaInfo['status']) {
$replication .= $this->replicationGui->getHtmlForReplicationStatusTable('replica');
}
}
$this->render('server/status/status/index', [
'is_data_loaded' => $this->data->dataLoaded,
'network_traffic' => $networkTraffic ?? null,
'uptime' => $uptime ?? null,
'start_time' => $startTime ?? null,
'traffic' => $traffic,
'connections' => $connections,
'is_primary' => $primaryInfo['status'],
'is_replica' => $replicaInfo['status'],
'replication' => $replication,
]);
}
private function getStartTime(): int
{
return (int) $this->dbi->fetchValue('SELECT UNIX_TIMESTAMP() - ' . $this->data->status['Uptime']);
}
/**
* @return array
*/
private function getTrafficInfo(): array
{
$hourFactor = 3600 / $this->data->status['Uptime'];
/** @var string[] $bytesReceived */
$bytesReceived = Util::formatByteDown($this->data->status['Bytes_received'], 3, 1);
/** @var string[] $bytesReceivedPerHour */
$bytesReceivedPerHour = Util::formatByteDown($this->data->status['Bytes_received'] * $hourFactor, 3, 1);
/** @var string[] $bytesSent */
$bytesSent = Util::formatByteDown($this->data->status['Bytes_sent'], 3, 1);
/** @var string[] $bytesSentPerHour */
$bytesSentPerHour = Util::formatByteDown($this->data->status['Bytes_sent'] * $hourFactor, 3, 1);
/** @var string[] $bytesTotal */
$bytesTotal = Util::formatByteDown(
$this->data->status['Bytes_received'] + $this->data->status['Bytes_sent'],
3,
1
);
/** @var string[] $bytesTotalPerHour */
$bytesTotalPerHour = Util::formatByteDown(
($this->data->status['Bytes_received'] + $this->data->status['Bytes_sent']) * $hourFactor,
3,
1
);
return [
[
'name' => __('Received'),
'number' => implode(' ', $bytesReceived),
'per_hour' => implode(' ', $bytesReceivedPerHour),
],
[
'name' => __('Sent'),
'number' => implode(' ', $bytesSent),
'per_hour' => implode(' ', $bytesSentPerHour),
],
[
'name' => __('Total'),
'number' => implode(' ', $bytesTotal),
'per_hour' => implode(' ', $bytesTotalPerHour),
],
];
}
/**
* @return array
*/
private function getConnectionsInfo(): array
{
$hourFactor = 3600 / $this->data->status['Uptime'];
$failedAttemptsPercentage = '---';
$abortedPercentage = '---';
if ($this->data->status['Connections'] > 0) {
$failedAttemptsPercentage = Util::formatNumber(
$this->data->status['Aborted_connects'] * 100 / $this->data->status['Connections'],
0,
2,
true
) . '%';
$abortedPercentage = Util::formatNumber(
$this->data->status['Aborted_clients'] * 100 / $this->data->status['Connections'],
0,
2,
true
) . '%';
}
return [
[
'name' => __('Max. concurrent connections'),
'number' => Util::formatNumber($this->data->status['Max_used_connections'], 0),
'per_hour' => '---',
'percentage' => '---',
],
[
'name' => __('Failed attempts'),
'number' => Util::formatNumber($this->data->status['Aborted_connects'], 4, 1, true),
'per_hour' => Util::formatNumber($this->data->status['Aborted_connects'] * $hourFactor, 4, 2, true),
'percentage' => $failedAttemptsPercentage,
],
[
'name' => __('Aborted'),
'number' => Util::formatNumber($this->data->status['Aborted_clients'], 4, 1, true),
'per_hour' => Util::formatNumber($this->data->status['Aborted_clients'] * $hourFactor, 4, 2, true),
'percentage' => $abortedPercentage,
],
[
'name' => __('Total'),
'number' => Util::formatNumber($this->data->status['Connections'], 4, 0),
'per_hour' => Util::formatNumber($this->data->status['Connections'] * $hourFactor, 4, 2),
'percentage' => Util::formatNumber(100, 0, 2) . '%',
],
];
}
}

View file

@ -1,520 +0,0 @@
<?php
/**
* Displays a list of server status variables
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Status;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Status\Data;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function __;
use function in_array;
use function is_numeric;
use function str_contains;
class VariablesController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $data);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = [
'flush' => $_POST['flush'] ?? null,
'filterAlert' => $_POST['filterAlert'] ?? null,
'filterText' => $_POST['filterText'] ?? null,
'filterCategory' => $_POST['filterCategory'] ?? null,
'dontFormat' => $_POST['dontFormat'] ?? null,
];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$this->addScriptFiles([
'server/status/variables.js',
'vendor/jquery/jquery.tablesorter.js',
'server/status/sorter.js',
]);
if (isset($params['flush'])) {
$this->flush($params['flush']);
}
if ($this->data->dataLoaded) {
$categories = [];
foreach ($this->data->sections as $sectionId => $sectionName) {
if (! isset($this->data->sectionUsed[$sectionId])) {
continue;
}
$categories[$sectionId] = [
'id' => $sectionId,
'name' => $sectionName,
'is_selected' => false,
];
if (empty($params['filterCategory']) || $params['filterCategory'] !== $sectionId) {
continue;
}
$categories[$sectionId]['is_selected'] = true;
}
$links = [];
foreach ($this->data->links as $sectionName => $sectionLinks) {
$links[$sectionName] = [
'name' => 'status_' . $sectionName,
'links' => $sectionLinks,
];
}
$descriptions = $this->getDescriptions();
$alerts = $this->getAlerts();
$variables = [];
foreach ($this->data->status as $name => $value) {
$variables[$name] = [
'name' => $name,
'value' => $value,
'is_numeric' => is_numeric($value),
'class' => $this->data->allocationMap[$name] ?? null,
'doc' => '',
'has_alert' => false,
'is_alert' => false,
'description' => $descriptions[$name] ?? '',
'description_doc' => [],
];
// Fields containing % are calculated,
// they can not be described in MySQL documentation
if (! str_contains($name, '%')) {
$variables[$name]['doc'] = Generator::linkToVarDocumentation(
$name,
$this->dbi->isMariaDB()
);
}
if (isset($alerts[$name])) {
$variables[$name]['has_alert'] = true;
if ($value > $alerts[$name]) {
$variables[$name]['is_alert'] = true;
}
}
if (! isset($this->data->links[$name])) {
continue;
}
foreach ($this->data->links[$name] as $linkName => $linkUrl) {
$variables[$name]['description_doc'][] = [
'name' => $linkName,
'url' => $linkUrl,
];
}
}
}
$this->render('server/status/variables/index', [
'is_data_loaded' => $this->data->dataLoaded,
'filter_text' => ! empty($params['filterText']) ? $params['filterText'] : '',
'is_only_alerts' => ! empty($params['filterAlert']),
'is_not_formatted' => ! empty($params['dontFormat']),
'categories' => $categories ?? [],
'links' => $links ?? [],
'variables' => $variables ?? [],
]);
}
/**
* Flush status variables if requested
*
* @param string $flush Variable name
*/
private function flush(string $flush): void
{
$flushCommands = [
'STATUS',
'TABLES',
'QUERY CACHE',
];
if (! in_array($flush, $flushCommands)) {
return;
}
$this->dbi->query('FLUSH ' . $flush . ';');
}
/**
* @return array
*/
private function getAlerts(): array
{
// name => max value before alert
return [
// lower is better
// variable => max value
'Aborted_clients' => 0,
'Aborted_connects' => 0,
'Binlog_cache_disk_use' => 0,
'Created_tmp_disk_tables' => 0,
'Handler_read_rnd' => 0,
'Handler_read_rnd_next' => 0,
'Innodb_buffer_pool_pages_dirty' => 0,
'Innodb_buffer_pool_reads' => 0,
'Innodb_buffer_pool_wait_free' => 0,
'Innodb_log_waits' => 0,
'Innodb_row_lock_time_avg' => 10, // ms
'Innodb_row_lock_time_max' => 50, // ms
'Innodb_row_lock_waits' => 0,
'Slow_queries' => 0,
'Delayed_errors' => 0,
'Select_full_join' => 0,
'Select_range_check' => 0,
'Sort_merge_passes' => 0,
'Opened_tables' => 0,
'Table_locks_waited' => 0,
'Qcache_lowmem_prunes' => 0,
'Qcache_free_blocks' => isset($this->data->status['Qcache_total_blocks'])
? $this->data->status['Qcache_total_blocks'] / 5
: 0,
'Slow_launch_threads' => 0,
// depends on Key_read_requests
// normally lower then 1:0.01
'Key_reads' => isset($this->data->status['Key_read_requests'])
? 0.01 * $this->data->status['Key_read_requests'] : 0,
// depends on Key_write_requests
// normally nearly 1:1
'Key_writes' => isset($this->data->status['Key_write_requests'])
? 0.9 * $this->data->status['Key_write_requests'] : 0,
'Key_buffer_fraction' => 0.5,
// alert if more than 95% of thread cache is in use
'Threads_cached' => isset($this->data->variables['thread_cache_size'])
? 0.95 * $this->data->variables['thread_cache_size'] : 0,
// higher is better
// variable => min value
//'Handler read key' => '> ',
];
}
/**
* Returns a list of variable descriptions
*
* @return array
*/
private function getDescriptions(): array
{
/**
* Messages are built using the message name
*/
return [
'Aborted_clients' => __(
'The number of connections that were aborted because the client died'
. ' without closing the connection properly.'
),
'Aborted_connects' => __('The number of failed attempts to connect to the MySQL server.'),
'Binlog_cache_disk_use' => __(
'The number of transactions that used the temporary binary log cache'
. ' but that exceeded the value of binlog_cache_size and used a'
. ' temporary file to store statements from the transaction.'
),
'Binlog_cache_use' => __('The number of transactions that used the temporary binary log cache.'),
'Connections' => __('The number of connection attempts (successful or not) to the MySQL server.'),
'Created_tmp_disk_tables' => __(
'The number of temporary tables on disk created automatically by'
. ' the server while executing statements. If'
. ' Created_tmp_disk_tables is big, you may want to increase the'
. ' tmp_table_size value to cause temporary tables to be'
. ' memory-based instead of disk-based.'
),
'Created_tmp_files' => __('How many temporary files mysqld has created.'),
'Created_tmp_tables' => __(
'The number of in-memory temporary tables created automatically'
. ' by the server while executing statements.'
),
'Delayed_errors' => __(
'The number of rows written with INSERT DELAYED for which some'
. ' error occurred (probably duplicate key).'
),
'Delayed_insert_threads' => __(
'The number of INSERT DELAYED handler threads in use. Every'
. ' different table on which one uses INSERT DELAYED gets'
. ' its own thread.'
),
'Delayed_writes' => __('The number of INSERT DELAYED rows written.'),
'Flush_commands' => __('The number of executed FLUSH statements.'),
'Handler_commit' => __('The number of internal COMMIT statements.'),
'Handler_delete' => __('The number of times a row was deleted from a table.'),
'Handler_discover' => __(
'The MySQL server can ask the NDB Cluster storage engine if it'
. ' knows about a table with a given name. This is called discovery.'
. ' Handler_discover indicates the number of time tables have been'
. ' discovered.'
),
'Handler_read_first' => __(
'The number of times the first entry was read from an index. If this'
. ' is high, it suggests that the server is doing a lot of full'
. ' index scans; for example, SELECT col1 FROM foo, assuming that'
. ' col1 is indexed.'
),
'Handler_read_key' => __(
'The number of requests to read a row based on a key. If this is'
. ' high, it is a good indication that your queries and tables'
. ' are properly indexed.'
),
'Handler_read_next' => __(
'The number of requests to read the next row in key order. This is'
. ' incremented if you are querying an index column with a range'
. ' constraint or if you are doing an index scan.'
),
'Handler_read_prev' => __(
'The number of requests to read the previous row in key order.'
. ' This read method is mainly used to optimize ORDER BY … DESC.'
),
'Handler_read_rnd' => __(
'The number of requests to read a row based on a fixed position.'
. ' This is high if you are doing a lot of queries that require'
. ' sorting of the result. You probably have a lot of queries that'
. ' require MySQL to scan whole tables or you have joins that'
. ' don\'t use keys properly.'
),
'Handler_read_rnd_next' => __(
'The number of requests to read the next row in the data file.'
. ' This is high if you are doing a lot of table scans. Generally'
. ' this suggests that your tables are not properly indexed or that'
. ' your queries are not written to take advantage of the indexes'
. ' you have.'
),
'Handler_rollback' => __('The number of internal ROLLBACK statements.'),
'Handler_update' => __('The number of requests to update a row in a table.'),
'Handler_write' => __('The number of requests to insert a row in a table.'),
'Innodb_buffer_pool_pages_data' => __('The number of pages containing data (dirty or clean).'),
'Innodb_buffer_pool_pages_dirty' => __('The number of pages currently dirty.'),
'Innodb_buffer_pool_pages_flushed' => __(
'The number of buffer pool pages that have been requested to be flushed.'
),
'Innodb_buffer_pool_pages_free' => __('The number of free pages.'),
'Innodb_buffer_pool_pages_latched' => __(
'The number of latched pages in InnoDB buffer pool. These are pages'
. ' currently being read or written or that can\'t be flushed or'
. ' removed for some other reason.'
),
'Innodb_buffer_pool_pages_misc' => __(
'The number of pages busy because they have been allocated for'
. ' administrative overhead such as row locks or the adaptive'
. ' hash index. This value can also be calculated as'
. ' Innodb_buffer_pool_pages_total - Innodb_buffer_pool_pages_free'
. ' - Innodb_buffer_pool_pages_data.'
),
'Innodb_buffer_pool_pages_total' => __('Total size of buffer pool, in pages.'),
'Innodb_buffer_pool_read_ahead_rnd' => __(
'The number of "random" read-aheads InnoDB initiated. This happens'
. ' when a query is to scan a large portion of a table but in'
. ' random order.'
),
'Innodb_buffer_pool_read_ahead_seq' => __(
'The number of sequential read-aheads InnoDB initiated. This'
. ' happens when InnoDB does a sequential full table scan.'
),
'Innodb_buffer_pool_read_requests' => __('The number of logical read requests InnoDB has done.'),
'Innodb_buffer_pool_reads' => __(
'The number of logical reads that InnoDB could not satisfy'
. ' from buffer pool and had to do a single-page read.'
),
'Innodb_buffer_pool_wait_free' => __(
'Normally, writes to the InnoDB buffer pool happen in the'
. ' background. However, if it\'s necessary to read or create a page'
. ' and no clean pages are available, it\'s necessary to wait for'
. ' pages to be flushed first. This counter counts instances of'
. ' these waits. If the buffer pool size was set properly, this'
. ' value should be small.'
),
'Innodb_buffer_pool_write_requests' => __('The number writes done to the InnoDB buffer pool.'),
'Innodb_data_fsyncs' => __('The number of fsync() operations so far.'),
'Innodb_data_pending_fsyncs' => __('The current number of pending fsync() operations.'),
'Innodb_data_pending_reads' => __('The current number of pending reads.'),
'Innodb_data_pending_writes' => __('The current number of pending writes.'),
'Innodb_data_read' => __('The amount of data read so far, in bytes.'),
'Innodb_data_reads' => __('The total number of data reads.'),
'Innodb_data_writes' => __('The total number of data writes.'),
'Innodb_data_written' => __('The amount of data written so far, in bytes.'),
'Innodb_dblwr_pages_written' => __(
'The number of pages that have been written for doublewrite operations.'
),
'Innodb_dblwr_writes' => __('The number of doublewrite operations that have been performed.'),
'Innodb_log_waits' => __(
'The number of waits we had because log buffer was too small and'
. ' we had to wait for it to be flushed before continuing.'
),
'Innodb_log_write_requests' => __('The number of log write requests.'),
'Innodb_log_writes' => __('The number of physical writes to the log file.'),
'Innodb_os_log_fsyncs' => __('The number of fsync() writes done to the log file.'),
'Innodb_os_log_pending_fsyncs' => __('The number of pending log file fsyncs.'),
'Innodb_os_log_pending_writes' => __('Pending log file writes.'),
'Innodb_os_log_written' => __('The number of bytes written to the log file.'),
'Innodb_pages_created' => __('The number of pages created.'),
'Innodb_page_size' => __(
'The compiled-in InnoDB page size (default 16KB). Many values are'
. ' counted in pages; the page size allows them to be easily'
. ' converted to bytes.'
),
'Innodb_pages_read' => __('The number of pages read.'),
'Innodb_pages_written' => __('The number of pages written.'),
'Innodb_row_lock_current_waits' => __('The number of row locks currently being waited for.'),
'Innodb_row_lock_time_avg' => __('The average time to acquire a row lock, in milliseconds.'),
'Innodb_row_lock_time' => __('The total time spent in acquiring row locks, in milliseconds.'),
'Innodb_row_lock_time_max' => __('The maximum time to acquire a row lock, in milliseconds.'),
'Innodb_row_lock_waits' => __('The number of times a row lock had to be waited for.'),
'Innodb_rows_deleted' => __('The number of rows deleted from InnoDB tables.'),
'Innodb_rows_inserted' => __('The number of rows inserted in InnoDB tables.'),
'Innodb_rows_read' => __('The number of rows read from InnoDB tables.'),
'Innodb_rows_updated' => __('The number of rows updated in InnoDB tables.'),
'Key_blocks_not_flushed' => __(
'The number of key blocks in the key cache that have changed but'
. ' haven\'t yet been flushed to disk. It used to be known as'
. ' Not_flushed_key_blocks.'
),
'Key_blocks_unused' => __(
'The number of unused blocks in the key cache. You can use this'
. ' value to determine how much of the key cache is in use.'
),
'Key_blocks_used' => __(
'The number of used blocks in the key cache. This value is a'
. ' high-water mark that indicates the maximum number of blocks'
. ' that have ever been in use at one time.'
),
'Key_buffer_fraction_%' => __('Percentage of used key cache (calculated value)'),
'Key_read_requests' => __('The number of requests to read a key block from the cache.'),
'Key_reads' => __(
'The number of physical reads of a key block from disk. If Key_reads'
. ' is big, then your key_buffer_size value is probably too small.'
. ' The cache miss rate can be calculated as'
. ' Key_reads/Key_read_requests.'
),
'Key_read_ratio_%' => __(
'Key cache miss calculated as rate of physical reads compared to read requests (calculated value)'
),
'Key_write_requests' => __('The number of requests to write a key block to the cache.'),
'Key_writes' => __('The number of physical writes of a key block to disk.'),
'Key_write_ratio_%' => __('Percentage of physical writes compared to write requests (calculated value)'),
'Last_query_cost' => __(
'The total cost of the last compiled query as computed by the query'
. ' optimizer. Useful for comparing the cost of different query'
. ' plans for the same query. The default value of 0 means that'
. ' no query has been compiled yet.'
),
'Max_used_connections' => __(
'The maximum number of connections that have been in use simultaneously since the server started.'
),
'Not_flushed_delayed_rows' => __('The number of rows waiting to be written in INSERT DELAYED queues.'),
'Opened_tables' => __(
'The number of tables that have been opened. If opened tables is'
. ' big, your table_open_cache value is probably too small.'
),
'Open_files' => __('The number of files that are open.'),
'Open_streams' => __('The number of streams that are open (used mainly for logging).'),
'Open_tables' => __('The number of tables that are open.'),
'Qcache_free_blocks' => __(
'The number of free memory blocks in query cache. High numbers can'
. ' indicate fragmentation issues, which may be solved by issuing'
. ' a FLUSH QUERY CACHE statement.'
),
'Qcache_free_memory' => __('The amount of free memory for query cache.'),
'Qcache_hits' => __('The number of cache hits.'),
'Qcache_inserts' => __('The number of queries added to the cache.'),
'Qcache_lowmem_prunes' => __(
'The number of queries that have been removed from the cache to'
. ' free up memory for caching new queries. This information can'
. ' help you tune the query cache size. The query cache uses a'
. ' least recently used (LRU) strategy to decide which queries'
. ' to remove from the cache.'
),
'Qcache_not_cached' => __(
'The number of non-cached queries (not cachable, or not cached due to the query_cache_type setting).'
),
'Qcache_queries_in_cache' => __('The number of queries registered in the cache.'),
'Qcache_total_blocks' => __('The total number of blocks in the query cache.'),
'Rpl_status' => __('The status of failsafe replication (not yet implemented).'),
'Select_full_join' => __(
'The number of joins that do not use indexes. If this value is'
. ' not 0, you should carefully check the indexes of your tables.'
),
'Select_full_range_join' => __('The number of joins that used a range search on a reference table.'),
'Select_range_check' => __(
'The number of joins without keys that check for key usage after'
. ' each row. (If this is not 0, you should carefully check the'
. ' indexes of your tables.)'
),
'Select_range' => __(
'The number of joins that used ranges on the first table. (It\'s'
. ' normally not critical even if this is big.)'
),
'Select_scan' => __('The number of joins that did a full scan of the first table.'),
'Slave_open_temp_tables' => __('The number of temporary tables currently open by the replica SQL thread.'),
'Slave_retried_transactions' => __(
'Total (since startup) number of times the replication replica SQL thread has retried transactions.'
),
'Slave_running' => __('This is ON if this server is a replica that is connected to a primary.'),
'Slow_launch_threads' => __(
'The number of threads that have taken more than slow_launch_time seconds to create.'
),
'Slow_queries' => __('The number of queries that have taken more than long_query_time seconds.'),
'Sort_merge_passes' => __(
'The number of merge passes the sort algorithm has had to do.'
. ' If this value is large, you should consider increasing the'
. ' value of the sort_buffer_size system variable.'
),
'Sort_range' => __('The number of sorts that were done with ranges.'),
'Sort_rows' => __('The number of sorted rows.'),
'Sort_scan' => __('The number of sorts that were done by scanning the table.'),
'Table_locks_immediate' => __('The number of times that a table lock was acquired immediately.'),
'Table_locks_waited' => __(
'The number of times that a table lock could not be acquired'
. ' immediately and a wait was needed. If this is high, and you have'
. ' performance problems, you should first optimize your queries,'
. ' and then either split your table or tables or use replication.'
),
'Threads_cached' => __(
'The number of threads in the thread cache. The cache hit rate can'
. ' be calculated as Threads_created/Connections. If this value is'
. ' red you should raise your thread_cache_size.'
),
'Threads_connected' => __('The number of currently open connections.'),
'Threads_created' => __(
'The number of threads created to handle connections. If'
. ' Threads_created is big, you may want to increase the'
. ' thread_cache_size value. (Normally this doesn\'t give a notable'
. ' performance improvement if you have a good thread'
. ' implementation.)'
),
'Threads_cache_hitrate_%' => __('Thread cache hit rate (calculated value)'),
'Threads_running' => __('The number of threads that are not sleeping.'),
];
}
}

View file

@ -1,110 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\UserGroups;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
/**
* Displays the 'User groups' sub page under 'Users' page.
*/
class UserGroupsController extends AbstractController
{
/** @var Relation */
private $relation;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Relation $relation,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->relation = $relation;
$this->dbi = $dbi;
}
public function __invoke(): void
{
$configurableMenusFeature = $this->relation->getRelationParameters()->configurableMenusFeature;
if ($configurableMenusFeature === null) {
return;
}
$this->addScriptFiles(['server/user_groups.js']);
/**
* Only allowed to superuser
*/
if (! $this->dbi->isSuperUser()) {
$this->response->addHTML(
Message::error(__('No Privileges'))->getDisplay()
);
return;
}
$this->response->addHTML('<div class="container-fluid">');
$this->render('server/privileges/subnav', [
'active' => 'user-groups',
'is_super_user' => $this->dbi->isSuperUser(),
]);
/**
* Delete user group
*/
if (! empty($_POST['deleteUserGroup'])) {
UserGroups::delete($configurableMenusFeature, $_POST['userGroup']);
}
/**
* Add a new user group
*/
if (! empty($_POST['addUserGroupSubmit'])) {
UserGroups::edit($configurableMenusFeature, $_POST['userGroup'], true);
}
/**
* Update a user group
*/
if (! empty($_POST['editUserGroupSubmit'])) {
UserGroups::edit($configurableMenusFeature, $_POST['userGroup']);
}
if (isset($_POST['viewUsers'])) {
// Display users belonging to a user group
$this->response->addHTML(UserGroups::getHtmlForListingUsersofAGroup(
$configurableMenusFeature,
$_POST['userGroup']
));
}
if (isset($_GET['addUserGroup'])) {
// Display add user group dialog
$this->response->addHTML(UserGroups::getHtmlToEditUserGroup($configurableMenusFeature));
} elseif (isset($_POST['editUserGroup'])) {
// Display edit user group dialog
$this->response->addHTML(UserGroups::getHtmlToEditUserGroup(
$configurableMenusFeature,
$_POST['userGroup']
));
} else {
// Display user groups table
$this->response->addHTML(UserGroups::getHtmlForUserGroupsTable($configurableMenusFeature));
}
$this->response->addHTML('</div>');
}
}

View file

@ -1,104 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\ConfigStorage\Features\ConfigurableMenusFeature;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function __;
use function sprintf;
use function strlen;
final class UserGroupsFormController extends AbstractController
{
/** @var Relation */
private $relation;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Relation $relation,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->relation = $relation;
$this->dbi = $dbi;
}
public function __invoke(): void
{
$this->response->setAjax(true);
if (! isset($_GET['username']) || strlen((string) $_GET['username']) === 0) {
$this->response->setRequestStatus(false);
$this->response->setHttpResponseCode(400);
$this->response->addJSON('message', __('Missing parameter:') . ' username');
return;
}
$username = $_GET['username'];
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
$checkUserPrivileges->getPrivileges();
$configurableMenusFeature = $this->relation->getRelationParameters()->configurableMenusFeature;
if ($configurableMenusFeature === null) {
$this->response->setRequestStatus(false);
$this->response->setHttpResponseCode(400);
$this->response->addJSON('message', __('User groups management is not enabled.'));
return;
}
$form = $this->getHtmlToChooseUserGroup($username, $configurableMenusFeature);
$this->response->addJSON('message', $form);
}
/**
* Displays a dropdown to select the user group with menu items configured to each of them.
*/
private function getHtmlToChooseUserGroup(
string $username,
ConfigurableMenusFeature $configurableMenusFeature
): string {
$groupTable = Util::backquote($configurableMenusFeature->database)
. '.' . Util::backquote($configurableMenusFeature->userGroups);
$userTable = Util::backquote($configurableMenusFeature->database)
. '.' . Util::backquote($configurableMenusFeature->users);
$sqlQuery = sprintf(
'SELECT `usergroup` FROM %s WHERE `username` = \'%s\'',
$userTable,
$this->dbi->escapeString($username)
);
$userGroup = $this->dbi->fetchValue($sqlQuery, 0, DatabaseInterface::CONNECT_CONTROL);
$allUserGroups = [];
$sqlQuery = 'SELECT DISTINCT `usergroup` FROM ' . $groupTable;
$result = $this->dbi->tryQueryAsControlUser($sqlQuery);
if ($result) {
while ($row = $result->fetchRow()) {
$allUserGroups[$row[0]] = $row[0];
}
}
return $this->template->render('server/privileges/choose_user_group', [
'all_user_groups' => $allUserGroups,
'user_group' => $userGroup,
'params' => ['username' => $username],
]);
}
}

View file

@ -1,62 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Variables;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function header;
use function implode;
final class GetVariableController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
/**
* @param array $params Request parameters
*/
public function __invoke(ServerRequest $request, array $params): void
{
if (! $this->response->isAjax()) {
return;
}
// Send with correct charset
header('Content-Type: text/html; charset=UTF-8');
// Do not use double quotes inside the query to avoid a problem
// when server is running in ANSI_QUOTES sql_mode
$varValue = $this->dbi->fetchSingleRow(
'SHOW GLOBAL VARIABLES WHERE Variable_name=\''
. $this->dbi->escapeString($params['name']) . '\';',
DatabaseInterface::FETCH_NUM
);
$json = [
'message' => $varValue[1],
];
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($params['name']);
if ($variableType === 'byte') {
/** @var string[] $bytes */
$bytes = Util::formatByteDown($varValue[1], 3, 3);
$json['message'] = implode(' ', $bytes);
}
$this->response->addJSON($json);
}
}

View file

@ -1,138 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server\Variables;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function __;
use function htmlspecialchars;
use function implode;
use function is_numeric;
use function mb_strtolower;
use function preg_match;
use function trim;
final class SetVariableController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
/**
* Handle the AJAX request for setting value for a single variable
*
* @param array $vars Request parameters
*/
public function __invoke(ServerRequest $request, array $vars): void
{
if (! $this->response->isAjax()) {
return;
}
$value = (string) $request->getParsedBodyParam('varValue');
$variableName = (string) $vars['name'];
$matches = [];
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($variableName);
if (
$variableType === 'byte' && preg_match(
'/^\s*(\d+(\.\d+)?)\s*(mb|kb|mib|kib|gb|gib)\s*$/i',
$value,
$matches
)
) {
$exp = [
'kb' => 1,
'kib' => 1,
'mb' => 2,
'mib' => 2,
'gb' => 3,
'gib' => 3,
];
$value = (float) $matches[1] * 1024 ** $exp[mb_strtolower($matches[3])];
} else {
$value = $this->dbi->escapeString($value);
}
if (! is_numeric($value)) {
$value = "'" . $value . "'";
}
$json = [];
if (! preg_match('/[^a-zA-Z0-9_]+/', $variableName)) {
$this->dbi->query('SET GLOBAL ' . $variableName . ' = ' . $value);
// Some values are rounded down etc.
$varValue = $this->dbi->fetchSingleRow(
'SHOW GLOBAL VARIABLES WHERE Variable_name="'
. $this->dbi->escapeString($variableName)
. '";',
DatabaseInterface::FETCH_NUM
);
[$formattedValue, $isHtmlFormatted] = $this->formatVariable($variableName, $varValue[1]);
if ($isHtmlFormatted === false) {
$json['variable'] = htmlspecialchars($formattedValue);
} else {
$json['variable'] = $formattedValue;
}
} else {
$this->response->setRequestStatus(false);
$json['error'] = __('Setting variable failed');
}
$this->response->addJSON($json);
}
/**
* Format Variable
*
* @param string $name variable name
* @param int|string $value variable value
*
* @return array formatted string and bool if string is HTML formatted
*/
private function formatVariable($name, $value): array
{
$isHtmlFormatted = false;
$formattedValue = $value;
if (is_numeric($value)) {
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($name);
if ($variableType === 'byte') {
$isHtmlFormatted = true;
/** @var string[] $bytes */
$bytes = Util::formatByteDown($value, 3, 3);
$formattedValue = trim(
$this->template->render(
'server/variables/format_variable',
[
'valueTitle' => Util::formatNumber($value, 0),
'value' => implode(' ', $bytes),
]
)
);
} else {
$formattedValue = Util::formatNumber($value, 0);
}
}
return [
$formattedValue,
$isHtmlFormatted,
];
}
}

View file

@ -1,137 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function implode;
use function in_array;
use function is_numeric;
use function str_replace;
use function strtolower;
use function trim;
/**
* Handles viewing and editing server variables
*/
class VariablesController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
{
parent::__construct($response, $template);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $errorUrl;
$params = ['filter' => $_GET['filter'] ?? null];
$errorUrl = Url::getFromRoute('/');
if ($this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$filterValue = ! empty($params['filter']) ? $params['filter'] : '';
$this->addScriptFiles(['server/variables.js']);
$variables = [];
$serverVarsResult = $this->dbi->tryQuery('SHOW SESSION VARIABLES;');
if ($serverVarsResult !== false) {
$serverVarsSession = $serverVarsResult->fetchAllKeyPair();
unset($serverVarsResult);
$serverVars = $this->dbi->fetchResult('SHOW GLOBAL VARIABLES;', 0, 1);
// list of static (i.e. non-editable) system variables
$staticVariables = ServerVariablesProvider::getImplementation()->getStaticVariables();
foreach ($serverVars as $name => $value) {
$hasSessionValue = isset($serverVarsSession[$name])
&& $serverVarsSession[$name] !== $value;
$docLink = Generator::linkToVarDocumentation(
$name,
$this->dbi->isMariaDB(),
str_replace('_', '&nbsp;', $name)
);
[$formattedValue, $isEscaped] = $this->formatVariable($name, $value);
if ($hasSessionValue) {
[$sessionFormattedValue] = $this->formatVariable($name, $serverVarsSession[$name]);
}
$variables[] = [
'name' => $name,
'is_editable' => ! in_array(strtolower($name), $staticVariables),
'doc_link' => $docLink,
'value' => $formattedValue,
'is_escaped' => $isEscaped,
'has_session_value' => $hasSessionValue,
'session_value' => $sessionFormattedValue ?? null,
];
}
}
$this->render('server/variables/index', [
'variables' => $variables,
'filter_value' => $filterValue,
'is_superuser' => $this->dbi->isSuperUser(),
'is_mariadb' => $this->dbi->isMariaDB(),
]);
}
/**
* Format Variable
*
* @param string $name variable name
* @param int|string $value variable value
*
* @return array formatted string and bool if string is HTML formatted
*/
private function formatVariable($name, $value): array
{
$isHtmlFormatted = false;
$formattedValue = $value;
if (is_numeric($value)) {
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($name);
if ($variableType === 'byte') {
$isHtmlFormatted = true;
/** @var string[] $bytes */
$bytes = Util::formatByteDown($value, 3, 3);
$formattedValue = trim(
$this->template->render(
'server/variables/format_variable',
[
'valueTitle' => Util::formatNumber($value, 0),
'value' => implode(' ', $bytes),
]
)
);
} else {
$formattedValue = Util::formatNumber($value, 0);
}
}
return [
$formattedValue,
$isHtmlFormatted,
];
}
}