Update website

This commit is contained in:
Guilhem Lavaux 2025-02-11 21:30:02 +01:00
parent 0a686aeb9a
commit c4ffa0f6ee
4360 changed files with 1727 additions and 718385 deletions

View file

@ -1,101 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function __;
use function strlen;
abstract class AbstractController
{
/** @var ResponseRenderer */
protected $response;
/** @var Template */
protected $template;
public function __construct(ResponseRenderer $response, Template $template)
{
$this->response = $response;
$this->template = $template;
}
/**
* @param array<string, mixed> $templateData
*/
protected function render(string $templatePath, array $templateData = []): void
{
$this->response->addHTML($this->template->render($templatePath, $templateData));
}
/**
* @param string[] $files
*/
protected function addScriptFiles(array $files): void
{
$header = $this->response->getHeader();
$scripts = $header->getScripts();
$scripts->addFiles($files);
}
protected function hasDatabase(): bool
{
global $db, $is_db, $errno, $dbi, $message;
if (isset($is_db) && $is_db) {
return true;
}
$is_db = false;
if (strlen($db) > 0) {
$is_db = $dbi->selectDb($db);
// This "Command out of sync" 2014 error may happen, for example
// after calling a MySQL procedure; at this point we can't select
// the db but it's not necessarily wrong
if ($dbi->getError() && $errno == 2014) {
$is_db = true;
unset($errno);
}
}
if (strlen($db) === 0 || ! $is_db) {
if ($this->response->isAjax()) {
$this->response->setRequestStatus(false);
$this->response->addJSON(
'message',
Message::error(__('No databases selected.'))
);
return false;
}
// Not a valid db name -> back to the welcome page
$params = ['reload' => '1'];
if (isset($message)) {
$params['message'] = $message;
}
$this->redirect('/', $params);
return false;
}
return $is_db;
}
/**
* @param array<string, mixed> $params
*/
protected function redirect(string $route, array $params = []): void
{
$uri = './index.php?route=' . $route . Url::getCommonRaw($params, '&');
Core::sendHeaderLocation($uri);
}
}

View file

@ -1,81 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\BrowseForeigners;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
/**
* Display selection for relational field values
*/
class BrowseForeignersController extends AbstractController
{
/** @var BrowseForeigners */
private $browseForeigners;
/** @var Relation */
private $relation;
public function __construct(
ResponseRenderer $response,
Template $template,
BrowseForeigners $browseForeigners,
Relation $relation
) {
parent::__construct($response, $template);
$this->browseForeigners = $browseForeigners;
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
/** @var string|null $database */
$database = $request->getParsedBodyParam('db');
/** @var string|null $table */
$table = $request->getParsedBodyParam('table');
/** @var string|null $field */
$field = $request->getParsedBodyParam('field');
/** @var string $fieldKey */
$fieldKey = $request->getParsedBodyParam('fieldkey', '');
/** @var string $data */
$data = $request->getParsedBodyParam('data', '');
/** @var string|null $foreignShowAll */
$foreignShowAll = $request->getParsedBodyParam('foreign_showAll');
/** @var string $foreignFilter */
$foreignFilter = $request->getParsedBodyParam('foreign_filter', '');
if (! isset($database, $table, $field)) {
return;
}
$this->response->getFooter()->setMinimal();
$header = $this->response->getHeader();
$header->disableMenuAndConsole();
$header->setBodyId('body_browse_foreigners');
$foreigners = $this->relation->getForeigners($database, $table);
$foreignLimit = $this->browseForeigners->getForeignLimit($foreignShowAll);
$foreignData = $this->relation->getForeignData(
$foreigners,
$field,
true,
$foreignFilter,
$foreignLimit ?? '',
true
);
$this->response->addHTML($this->browseForeigners->getHtmlForRelationalFieldSelection(
$database,
$table,
$field,
$foreignData,
$fieldKey,
$data
));
}
}

View file

@ -1,105 +0,0 @@
<?php
/**
* Simple script to set correct charset for changelog
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use function __;
use function array_keys;
use function file_get_contents;
use function htmlspecialchars;
use function is_readable;
use function ob_get_clean;
use function ob_start;
use function preg_replace;
use function printf;
use function readgzfile;
use function substr;
class ChangeLogController extends AbstractController
{
public function __invoke(): void
{
$this->response->disable();
$this->response->getHeader()->sendHttpHeaders();
$filename = CHANGELOG_FILE;
/**
* Read changelog.
*/
// Check if the file is available, some distributions remove these.
if (! @is_readable($filename)) {
printf(
__(
'The %s file is not available on this system, please visit %s for more information.'
),
$filename,
'<a href="https://www.phpmyadmin.net/">phpmyadmin.net</a>'
);
return;
}
// Test if the if is in a compressed format
if (substr($filename, -3) === '.gz') {
ob_start();
readgzfile($filename);
$changelog = ob_get_clean();
} else {
$changelog = file_get_contents($filename);
}
/**
* Whole changelog in variable.
*/
$changelog = htmlspecialchars((string) $changelog);
$github_url = 'https://github.com/phpmyadmin/phpmyadmin/';
$faq_url = 'https://docs.phpmyadmin.net/en/latest/faq.html';
$replaces = [
'@(https?://[./a-zA-Z0-9.-_-]*[/a-zA-Z0-9_])@' => '<a href="url.php?url=\\1">\\1</a>',
// mail address
'/([0-9]{4}-[0-9]{2}-[0-9]{2}) (.+[^ ]) +&lt;(.*@.*)&gt;/i' => '\\1 <a href="mailto:\\3">\\2</a>',
// FAQ entries
'/FAQ ([0-9]+)\.([0-9a-z]+)/i' => '<a href="url.php?url=' . $faq_url . '#faq\\1-\\2">FAQ \\1.\\2</a>',
// GitHub issues
'/issue\s*#?([0-9]{4,5}) /i' => '<a href="url.php?url=' . $github_url . 'issues/\\1">issue #\\1</a> ',
// CVE/CAN entries
'/((CAN|CVE)-[0-9]+-[0-9]+)/' => '<a href="url.php?url='
. 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=\\1">\\1</a>',
// PMASAentries
'/(PMASA-[0-9]+-[0-9]+)/' => '<a href="url.php?url=https://www.phpmyadmin.net/security/\\1/">\\1</a>',
// Highlight releases (with links)
'/([0-9]+)\.([0-9]+)\.([0-9]+)\.0 (\([0-9-]+\))/' => '<a id="\\1_\\2_\\3"></a>'
. '<a href="url.php?url=' . $github_url . 'commits/RELEASE_\\1_\\2_\\3">'
. '\\1.\\2.\\3.0 \\4</a>',
'/([0-9]+)\.([0-9]+)\.([0-9]+)\.([1-9][0-9]*) (\([0-9-]+\))/' => '<a id="\\1_\\2_\\3_\\4"></a>'
. '<a href="url.php?url=' . $github_url . 'commits/RELEASE_\\1_\\2_\\3_\\4">'
. '\\1.\\2.\\3.\\4 \\5</a>',
// Highlight releases (not linkable)
'/( ### )(.*)/' => '\\1<b>\\2</b>',
// Links target and rel
'/a href="/' => 'a target="_blank" rel="noopener noreferrer" href="',
];
$this->response->header('Content-type: text/html; charset=utf-8');
echo $this->template->render('changelog', [
'changelog' => preg_replace(array_keys($replaces), $replaces, $changelog),
]);
}
}

View file

@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use const SQL_DIR;
/**
* Displays status of phpMyAdmin configuration storage
*/
class CheckRelationsController extends AbstractController
{
/** @var Relation */
private $relation;
public function __construct(ResponseRenderer $response, Template $template, Relation $relation)
{
parent::__construct($response, $template);
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
global $db, $cfg;
/** @var string|null $createPmaDb */
$createPmaDb = $request->getParsedBodyParam('create_pmadb');
/** @var string|null $fixAllPmaDb */
$fixAllPmaDb = $request->getParsedBodyParam('fixall_pmadb');
/** @var string|null $fixPmaDb */
$fixPmaDb = $request->getParsedBodyParam('fix_pmadb');
$cfgStorageDbName = $this->relation->getConfigurationStorageDbName();
// If request for creating the pmadb
if (isset($createPmaDb) && $this->relation->createPmaDatabase($cfgStorageDbName)) {
$this->relation->fixPmaTables($cfgStorageDbName);
}
// If request for creating all PMA tables.
if (isset($fixAllPmaDb)) {
$this->relation->fixPmaTables($db);
}
// If request for creating missing PMA tables.
if (isset($fixPmaDb)) {
$relationParameters = $this->relation->getRelationParameters();
$this->relation->fixPmaTables((string) $relationParameters->db);
}
// Do not use any previous $relationParameters value as it could have changed after a successful fixPmaTables()
$relationParameters = $this->relation->getRelationParameters();
$this->render('relation/check_relations', [
'db' => $db,
'zero_conf' => $cfg['ZeroConf'],
'relation_parameters' => $relationParameters->toArray(),
'sql_dir' => SQL_DIR,
'config_storage_database_name' => $cfgStorageDbName,
'are_config_storage_tables_defined' => $this->relation->arePmadbTablesDefined(),
]);
}
}

View file

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Config;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
final class CollationConnectionController extends AbstractController
{
/** @var Config */
private $config;
public function __construct(ResponseRenderer $response, Template $template, Config $config)
{
parent::__construct($response, $template);
$this->config = $config;
}
public function __invoke(): void
{
$this->config->setUserValue(
null,
'DefaultConnectionCollation',
$_POST['collation_connection'],
'utf8mb4_unicode_ci'
);
$this->response->header('Location: index.php?route=/' . Url::getCommonRaw([], '&'));
}
}

View file

@ -1,40 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
final class ColumnController 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(ServerRequest $request): void
{
/** @var string|null $db */
$db = $request->getParsedBodyParam('db');
/** @var string|null $table */
$table = $request->getParsedBodyParam('table');
if (! isset($db, $table)) {
$this->response->setRequestStatus(false);
$this->response->addJSON(['message' => Message::error()]);
return;
}
$this->response->addJSON(['columns' => $this->dbi->getColumnNames($db, $table)]);
}
}

View file

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Config;
use PhpMyAdmin\Config;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
final class GetConfigController extends AbstractController
{
/** @var Config */
private $config;
public function __construct(ResponseRenderer $response, Template $template, Config $config)
{
parent::__construct($response, $template);
$this->config = $config;
}
public function __invoke(ServerRequest $request): void
{
/** @var string|null $key */
$key = $request->getParsedBodyParam('key');
if (! isset($key)) {
$this->response->setRequestStatus(false);
$this->response->addJSON(['message' => Message::error()]);
return;
}
$this->response->addJSON(['value' => $this->config->get($key)]);
}
}

View file

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Config;
use PhpMyAdmin\Config;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function json_decode;
final class SetConfigController extends AbstractController
{
/** @var Config */
private $config;
public function __construct(ResponseRenderer $response, Template $template, Config $config)
{
parent::__construct($response, $template);
$this->config = $config;
}
public function __invoke(ServerRequest $request): void
{
/** @var string|null $key */
$key = $request->getParsedBodyParam('key');
/** @var string|null $value */
$value = $request->getParsedBodyParam('value');
if (! isset($key, $value)) {
$this->response->setRequestStatus(false);
$this->response->addJSON(['message' => Message::error()]);
return;
}
$result = $this->config->setUserValue(null, $key, json_decode($value));
if ($result === true) {
return;
}
$this->response->setRequestStatus(false);
$this->response->addJSON(['message' => $result]);
}
}

View file

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

View file

@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\CentralColumns;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Database\CentralColumns;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
final class PopulateColumnsController extends AbstractController
{
/** @var CentralColumns */
private $centralColumns;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
CentralColumns $centralColumns
) {
parent::__construct($response, $template, $db);
$this->centralColumns = $centralColumns;
}
public function __invoke(): void
{
$columns = $this->centralColumns->getColumnsNotInCentralList($this->db, $_POST['selectedTable']);
$this->render('database/central_columns/populate_columns', ['columns' => $columns]);
}
}

View file

@ -1,280 +0,0 @@
<?php
/**
* Central Columns view/edit
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\CentralColumns;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
use function is_bool;
use function is_numeric;
use function parse_str;
use function sprintf;
class CentralColumnsController extends AbstractController
{
/** @var CentralColumns */
private $centralColumns;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
CentralColumns $centralColumns
) {
parent::__construct($response, $template, $db);
$this->centralColumns = $centralColumns;
}
public function __invoke(): void
{
global $cfg, $db, $message, $pos, $num_cols;
if (isset($_POST['edit_save'])) {
echo $this->editSave([
'col_name' => $_POST['col_name'] ?? null,
'orig_col_name' => $_POST['orig_col_name'] ?? null,
'col_default' => $_POST['col_default'] ?? null,
'col_default_sel' => $_POST['col_default_sel'] ?? null,
'col_extra' => $_POST['col_extra'] ?? null,
'col_isNull' => $_POST['col_isNull'] ?? null,
'col_length' => $_POST['col_length'] ?? null,
'col_attribute' => $_POST['col_attribute'] ?? null,
'col_type' => $_POST['col_type'] ?? null,
'collation' => $_POST['collation'] ?? null,
]);
return;
}
if (isset($_POST['add_new_column'])) {
$tmp_msg = $this->addNewColumn([
'col_name' => $_POST['col_name'] ?? null,
'col_default' => $_POST['col_default'] ?? null,
'col_default_sel' => $_POST['col_default_sel'] ?? null,
'col_extra' => $_POST['col_extra'] ?? null,
'col_isNull' => $_POST['col_isNull'] ?? null,
'col_length' => $_POST['col_length'] ?? null,
'col_attribute' => $_POST['col_attribute'] ?? null,
'col_type' => $_POST['col_type'] ?? null,
'collation' => $_POST['collation'] ?? null,
]);
}
if (isset($_POST['getColumnList'])) {
$this->response->addJSON('message', $this->getColumnList([
'cur_table' => $_POST['cur_table'] ?? null,
]));
return;
}
if (isset($_POST['add_column'])) {
$tmp_msg = $this->addColumn([
'table-select' => $_POST['table-select'] ?? null,
'column-select' => $_POST['column-select'] ?? null,
]);
}
$this->addScriptFiles([
'vendor/jquery/jquery.uitablefilter.js',
'vendor/jquery/jquery.tablesorter.js',
'database/central_columns.js',
]);
if (isset($_POST['edit_central_columns_page'])) {
$this->editPage([
'selected_fld' => $_POST['selected_fld'] ?? null,
'db' => $_POST['db'] ?? null,
]);
return;
}
if (isset($_POST['multi_edit_central_column_save'])) {
$message = $this->updateMultipleColumn([
'db' => $_POST['db'] ?? null,
'orig_col_name' => $_POST['orig_col_name'] ?? null,
'field_name' => $_POST['field_name'] ?? null,
'field_default_type' => $_POST['field_default_type'] ?? null,
'field_default_value' => $_POST['field_default_value'] ?? null,
'field_length' => $_POST['field_length'] ?? null,
'field_attribute' => $_POST['field_attribute'] ?? null,
'field_type' => $_POST['field_type'] ?? null,
'field_collation' => $_POST['field_collation'] ?? null,
'field_null' => $_POST['field_null'] ?? null,
'col_extra' => $_POST['col_extra'] ?? null,
]);
if (! is_bool($message)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $message);
}
}
if (isset($_POST['delete_save'])) {
$tmp_msg = $this->deleteSave([
'db' => $_POST['db'] ?? null,
'col_name' => $_POST['col_name'] ?? null,
]);
}
$this->main([
'pos' => $_POST['pos'] ?? null,
'total_rows' => $_POST['total_rows'] ?? null,
]);
$pos = 0;
if (isset($_POST['pos']) && is_numeric($_POST['pos'])) {
$pos = (int) $_POST['pos'];
}
$num_cols = $this->centralColumns->getColumnsCount($db, $pos, (int) $cfg['MaxRows']);
$message = Message::success(
sprintf(__('Showing rows %1$s - %2$s.'), $pos + 1, $pos + $num_cols)
);
if (! isset($tmp_msg) || $tmp_msg === true) {
return;
}
$message = $tmp_msg;
}
/**
* @param array $params Request parameters
*/
public function main(array $params): void
{
global $text_dir;
if (! empty($params['total_rows']) && is_numeric($params['total_rows'])) {
$totalRows = (int) $params['total_rows'];
} else {
$totalRows = $this->centralColumns->getCount($this->db);
}
$pos = 0;
if (isset($params['pos']) && is_numeric($params['pos'])) {
$pos = (int) $params['pos'];
}
$variables = $this->centralColumns->getTemplateVariablesForMain($this->db, $totalRows, $pos, $text_dir);
$this->render('database/central_columns/main', $variables);
}
/**
* @param array $params Request parameters
*
* @return array JSON
*/
public function getColumnList(array $params): array
{
return $this->centralColumns->getListRaw($this->db, $params['cur_table'] ?? '');
}
/**
* @param array $params Request parameters
*
* @return true|Message
*/
public function editSave(array $params)
{
$columnDefault = $params['col_default'];
if ($columnDefault === 'NONE' && $params['col_default_sel'] !== 'USER_DEFINED') {
$columnDefault = '';
}
return $this->centralColumns->updateOneColumn(
$this->db,
$params['orig_col_name'],
$params['col_name'],
$params['col_type'],
$params['col_attribute'],
$params['col_length'],
isset($params['col_isNull']) ? 1 : 0,
$params['collation'],
$params['col_extra'] ?? '',
$columnDefault
);
}
/**
* @param array $params Request parameters
*
* @return true|Message
*/
public function addNewColumn(array $params)
{
$columnDefault = $params['col_default'];
if ($columnDefault === 'NONE' && $params['col_default_sel'] !== 'USER_DEFINED') {
$columnDefault = '';
}
return $this->centralColumns->updateOneColumn(
$this->db,
'',
$params['col_name'],
$params['col_type'],
$params['col_attribute'],
$params['col_length'],
isset($params['col_isNull']) ? 1 : 0,
$params['collation'],
$params['col_extra'] ?? '',
$columnDefault
);
}
/**
* @param array $params Request parameters
*
* @return true|Message
*/
public function addColumn(array $params)
{
return $this->centralColumns->syncUniqueColumns(
[$params['column-select']],
false,
$params['table-select']
);
}
/**
* @param array $params Request parameters
*/
public function editPage(array $params): void
{
$rows = $this->centralColumns->getHtmlForEditingPage($params['selected_fld'], $params['db']);
$this->render('database/central_columns/edit', ['rows' => $rows]);
}
/**
* @param array $params Request parameters
*
* @return true|Message
*/
public function updateMultipleColumn(array $params)
{
return $this->centralColumns->updateMultipleColumn($params);
}
/**
* @param array $params Request parameters
*
* @return true|Message
*/
public function deleteSave(array $params)
{
$name = [];
parse_str($params['col_name'], $name);
return $this->centralColumns->deleteColumnsFromList($params['db'], $name['selected_fld'], false);
}
}

View file

@ -1,122 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Index;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Util;
use function is_array;
use function str_replace;
class DataDictionaryController extends AbstractController
{
/** @var Relation */
private $relation;
/** @var Transformations */
private $transformations;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Relation $relation,
Transformations $transformations,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->relation = $relation;
$this->transformations = $transformations;
$this->dbi = $dbi;
}
public function __invoke(): void
{
Util::checkParameters(['db'], true);
$relationParameters = $this->relation->getRelationParameters();
$comment = $this->relation->getDbComment($this->db);
$this->dbi->selectDb($this->db);
$tablesNames = $this->dbi->getTables($this->db);
$tables = [];
foreach ($tablesNames as $tableName) {
$showComment = (string) $this->dbi->getTable($this->db, $tableName)->getStatusInfo('TABLE_COMMENT');
[, $primaryKeys] = Util::processIndexData(
$this->dbi->getTableIndexes($this->db, $tableName)
);
[$foreigners, $hasRelation] = $this->relation->getRelationsAndStatus(
$relationParameters->relationFeature !== null,
$this->db,
$tableName
);
$columnsComments = $this->relation->getComments($this->db, $tableName);
$columns = $this->dbi->getColumns($this->db, $tableName);
$rows = [];
foreach ($columns as $row) {
$extractedColumnSpec = Util::extractColumnSpec($row['Type']);
$relation = '';
if ($hasRelation) {
$foreigner = $this->relation->searchColumnInForeigners($foreigners, $row['Field']);
if (is_array($foreigner) && isset($foreigner['foreign_table'], $foreigner['foreign_field'])) {
$relation = $foreigner['foreign_table'];
$relation .= ' -> ';
$relation .= $foreigner['foreign_field'];
}
}
$mime = '';
if ($relationParameters->browserTransformationFeature !== null) {
$mimeMap = $this->transformations->getMime($this->db, $tableName, true);
if (is_array($mimeMap) && isset($mimeMap[$row['Field']]['mimetype'])) {
$mime = str_replace('_', '/', $mimeMap[$row['Field']]['mimetype']);
}
}
$rows[$row['Field']] = [
'name' => $row['Field'],
'has_primary_key' => isset($primaryKeys[$row['Field']]),
'type' => $extractedColumnSpec['type'],
'print_type' => $extractedColumnSpec['print_type'],
'is_nullable' => $row['Null'] !== '' && $row['Null'] !== 'NO',
'default' => $row['Default'] ?? null,
'comment' => $columnsComments[$row['Field']] ?? '',
'mime' => $mime,
'relation' => $relation,
];
}
$tables[$tableName] = [
'name' => $tableName,
'comment' => $showComment,
'has_relation' => $hasRelation,
'has_mime' => $relationParameters->browserTransformationFeature !== null,
'columns' => $rows,
'indexes' => Index::getFromTable($tableName, $this->db),
];
}
$this->render('database/data_dictionary/index', [
'database' => $this->db,
'comment' => $comment,
'tables' => $tables,
]);
}
}

View file

@ -1,250 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\Designer;
use PhpMyAdmin\Database\Designer\Common as DesignerCommon;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function htmlspecialchars;
use function in_array;
use function sprintf;
class DesignerController extends AbstractController
{
/** @var Designer */
private $databaseDesigner;
/** @var DesignerCommon */
private $designerCommon;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Designer $databaseDesigner,
DesignerCommon $designerCommon
) {
parent::__construct($response, $template, $db);
$this->databaseDesigner = $databaseDesigner;
$this->designerCommon = $designerCommon;
}
public function __invoke(): void
{
global $db, $script_display_field, $tab_column, $tables_all_keys, $tables_pk_or_unique_keys;
global $success, $page, $message, $display_page, $selected_page, $tab_pos, $fullTableNames, $script_tables;
global $script_contr, $params, $tables, $num_tables, $total_num_tables, $sub_part;
global $tooltip_truename, $tooltip_aliasname, $pos, $classes_side_menu, $cfg, $errorUrl;
if (isset($_POST['dialog'])) {
if ($_POST['dialog'] === 'edit') {
$html = $this->databaseDesigner->getHtmlForEditOrDeletePages($_POST['db'], 'editPage');
} elseif ($_POST['dialog'] === 'delete') {
$html = $this->databaseDesigner->getHtmlForEditOrDeletePages($_POST['db'], 'deletePage');
} elseif ($_POST['dialog'] === 'save_as') {
$html = $this->databaseDesigner->getHtmlForPageSaveAs($_POST['db']);
} elseif ($_POST['dialog'] === 'export') {
$html = $this->databaseDesigner->getHtmlForSchemaExport($_POST['db'], $_POST['selected_page']);
} elseif ($_POST['dialog'] === 'add_table') {
// Pass the db and table to the getTablesInfo so we only have the table we asked for
$script_display_field = $this->designerCommon->getTablesInfo($_POST['db'], $_POST['table']);
$tab_column = $this->designerCommon->getColumnsInfo($script_display_field);
$tables_all_keys = $this->designerCommon->getAllKeys($script_display_field);
$tables_pk_or_unique_keys = $this->designerCommon->getPkOrUniqueKeys($script_display_field);
$html = $this->databaseDesigner->getDatabaseTables(
$_POST['db'],
$script_display_field,
[],
-1,
$tab_column,
$tables_all_keys,
$tables_pk_or_unique_keys
);
}
if (! empty($html)) {
$this->response->addHTML($html);
}
return;
}
if (isset($_POST['operation'])) {
if ($_POST['operation'] === 'deletePage') {
$success = $this->designerCommon->deletePage($_POST['selected_page']);
$this->response->setRequestStatus($success);
} elseif ($_POST['operation'] === 'savePage') {
if ($_POST['save_page'] === 'same') {
$page = $_POST['selected_page'];
} elseif ($this->designerCommon->getPageExists($_POST['selected_value'])) {
$this->response->addJSON(
'message',
sprintf(
/* l10n: The user tries to save a page with an existing name in Designer */
__('There already exists a page named "%s" please rename it to something else.'),
htmlspecialchars($_POST['selected_value'])
)
);
$this->response->setRequestStatus(false);
return;
} else {
$page = $this->designerCommon->createNewPage($_POST['selected_value'], $_POST['db']);
$this->response->addJSON('id', $page);
}
$success = $this->designerCommon->saveTablePositions($page);
$this->response->setRequestStatus($success);
} elseif ($_POST['operation'] === 'setDisplayField') {
[
$success,
$message,
] = $this->designerCommon->saveDisplayField($_POST['db'], $_POST['table'], $_POST['field']);
$this->response->setRequestStatus($success);
$this->response->addJSON('message', $message);
} elseif ($_POST['operation'] === 'addNewRelation') {
[$success, $message] = $this->designerCommon->addNewRelation(
$_POST['db'],
$_POST['T1'],
$_POST['F1'],
$_POST['T2'],
$_POST['F2'],
$_POST['on_delete'],
$_POST['on_update'],
$_POST['DB1'],
$_POST['DB2']
);
$this->response->setRequestStatus($success);
$this->response->addJSON('message', $message);
} elseif ($_POST['operation'] === 'removeRelation') {
[$success, $message] = $this->designerCommon->removeRelation(
$_POST['T1'],
$_POST['F1'],
$_POST['T2'],
$_POST['F2']
);
$this->response->setRequestStatus($success);
$this->response->addJSON('message', $message);
} elseif ($_POST['operation'] === 'save_setting_value') {
$success = $this->designerCommon->saveSetting($_POST['index'], $_POST['value']);
$this->response->setRequestStatus($success);
}
return;
}
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$script_display_field = $this->designerCommon->getTablesInfo();
$display_page = -1;
$selected_page = null;
$visualBuilderMode = isset($_GET['query']);
if ($visualBuilderMode) {
$display_page = $this->designerCommon->getDefaultPage($_GET['db']);
} elseif (! empty($_GET['page'])) {
$display_page = $_GET['page'];
} else {
$display_page = $this->designerCommon->getLoadingPage($_GET['db']);
}
if ($display_page != -1) {
$selected_page = $this->designerCommon->getPageName($display_page);
}
$tab_pos = $this->designerCommon->getTablePositions($display_page);
$fullTableNames = [];
foreach ($script_display_field as $designerTable) {
$fullTableNames[] = $designerTable->getDbTableString();
}
foreach ($tab_pos as $position) {
if (in_array($position['dbName'] . '.' . $position['tableName'], $fullTableNames)) {
continue;
}
$designerTables = $this->designerCommon->getTablesInfo($position['dbName'], $position['tableName']);
foreach ($designerTables as $designerTable) {
$script_display_field[] = $designerTable;
}
}
$tab_column = $this->designerCommon->getColumnsInfo($script_display_field);
$script_tables = $this->designerCommon->getScriptTabs($script_display_field);
$tables_pk_or_unique_keys = $this->designerCommon->getPkOrUniqueKeys($script_display_field);
$tables_all_keys = $this->designerCommon->getAllKeys($script_display_field);
$classes_side_menu = $this->databaseDesigner->returnClassNamesFromMenuButtons();
$script_contr = $this->designerCommon->getScriptContr($script_display_field);
$params = ['lang' => $GLOBALS['lang']];
if (isset($_GET['db'])) {
$params['db'] = $_GET['db'];
}
$this->response->getFooter()->setMinimal();
$header = $this->response->getHeader();
$header->setBodyId('designer_body');
$this->addScriptFiles([
'designer/database.js',
'designer/objects.js',
'designer/page.js',
'designer/history.js',
'designer/move.js',
'designer/init.js',
]);
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
// Embed some data into HTML, later it will be read
// by designer/init.js and converted to JS variables.
$this->response->addHTML(
$this->databaseDesigner->getHtmlForMain(
$db,
$_GET['db'],
$script_display_field,
$script_tables,
$script_contr,
$script_display_field,
$display_page,
$visualBuilderMode,
$selected_page,
$classes_side_menu,
$tab_pos,
$tab_column,
$tables_all_keys,
$tables_pk_or_unique_keys
)
);
$this->response->addHTML('<div id="PMA_disable_floating_menubar"></div>');
}
}

View file

@ -1,86 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\Events;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function strlen;
final class EventsController extends AbstractController
{
/** @var Events */
private $events;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Events $events,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->events = $events;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $tables, $num_tables, $total_num_tables, $sub_part, $errors, $text_dir;
global $tooltip_truename, $tooltip_aliasname, $pos, $cfg, $errorUrl;
$this->addScriptFiles(['database/events.js']);
if (! $this->response->isAjax()) {
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
} elseif (strlen($db) > 0) {
$this->dbi->selectDb($db);
}
/**
* Keep a list of errors that occurred while
* processing an 'Add' or 'Edit' operation.
*/
$errors = [];
$this->events->handleEditor();
$this->events->export();
$items = $this->dbi->getEvents($db);
$this->render('database/events/index', [
'db' => $db,
'items' => $items,
'has_privilege' => Util::currentUserHasPrivilege('EVENT', $db),
'scheduler_state' => $this->events->getEventSchedulerStatus(),
'text_dir' => $text_dir,
'is_ajax' => $this->response->isAjax() && empty($_REQUEST['ajax_page_request']),
]);
}
}

View file

@ -1,166 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\Export;
use PhpMyAdmin\Export\Options;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function array_merge;
use function is_array;
final class ExportController extends AbstractController
{
/** @var Export */
private $export;
/** @var Options */
private $exportOptions;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Export $export,
Options $exportOptions
) {
parent::__construct($response, $template, $db);
$this->export = $export;
$this->exportOptions = $exportOptions;
}
public function __invoke(): void
{
global $db, $table, $sub_part, $urlParams, $sql_query;
global $tables, $num_tables, $total_num_tables, $tooltip_truename;
global $tooltip_aliasname, $pos, $table_select, $unlim_num_rows, $cfg, $errorUrl;
$pageSettings = new PageSettings('Export');
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
$pageSettingsHtml = $pageSettings->getHTML();
$this->addScriptFiles(['export.js']);
// $sub_part is used in Util::getDbInfo() to see if we are coming from
// /database/export, in which case we don't obey $cfg['MaxTableList']
$sub_part = '_export';
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$urlParams['goto'] = Url::getFromRoute('/database/export');
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part);
// exit if no tables in db found
if ($num_tables < 1) {
$this->response->addHTML(
Message::error(__('No tables found in database.'))->getDisplay()
);
return;
}
if (! empty($_POST['selected_tbl']) && empty($table_select)) {
$table_select = $_POST['selected_tbl'];
}
$tablesForMultiValues = [];
foreach ($tables as $each_table) {
if (isset($_POST['table_select']) && is_array($_POST['table_select'])) {
$is_checked = $this->export->getCheckedClause($each_table['Name'], $_POST['table_select']);
} elseif (isset($table_select)) {
$is_checked = $this->export->getCheckedClause($each_table['Name'], $table_select);
} else {
$is_checked = true;
}
if (isset($_POST['table_structure']) && is_array($_POST['table_structure'])) {
$structure_checked = $this->export->getCheckedClause($each_table['Name'], $_POST['table_structure']);
} else {
$structure_checked = $is_checked;
}
if (isset($_POST['table_data']) && is_array($_POST['table_data'])) {
$data_checked = $this->export->getCheckedClause($each_table['Name'], $_POST['table_data']);
} else {
$data_checked = $is_checked;
}
$tablesForMultiValues[] = [
'name' => $each_table['Name'],
'is_checked_select' => $is_checked,
'is_checked_structure' => $structure_checked,
'is_checked_data' => $data_checked,
];
}
if (! isset($sql_query)) {
$sql_query = '';
}
if (! isset($unlim_num_rows)) {
$unlim_num_rows = 0;
}
$isReturnBackFromRawExport = isset($_POST['export_type']) && $_POST['export_type'] === 'raw';
if (isset($_POST['raw_query']) || $isReturnBackFromRawExport) {
$export_type = 'raw';
} else {
$export_type = 'database';
}
$GLOBALS['single_table'] = $_POST['single_table'] ?? $_GET['single_table'] ?? $GLOBALS['single_table'] ?? null;
$exportList = Plugins::getExport($export_type, 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->exportOptions->getOptions(
$export_type,
$db,
$table,
$sql_query,
$num_tables,
$unlim_num_rows,
$exportList
);
$this->render('database/export/index', array_merge($options, [
'page_settings_error_html' => $pageSettingsErrorHtml,
'page_settings_html' => $pageSettingsHtml,
'structure_or_data_forced' => $_POST['structure_or_data_forced'] ?? 0,
'tables' => $tablesForMultiValues,
]));
}
}

View file

@ -1,134 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\Config\PageSettings;
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, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $table, $tables, $num_tables, $total_num_tables, $cfg;
global $tooltip_truename, $tooltip_aliasname, $pos, $sub_part, $SESSION_KEY, $errorUrl;
$pageSettings = new PageSettings('Import');
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
$pageSettingsHtml = $pageSettings->getHTML();
$this->addScriptFiles(['import.js']);
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
[$SESSION_KEY, $uploadId] = Ajax::uploadProgressSetup();
$importList = Plugins::getImport('database');
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' => 'database',
'db' => $db,
];
$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('database/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,21 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\MultiTableQuery;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Database\MultiTableQuery;
final class QueryController extends AbstractController
{
public function __invoke(): void
{
$params = [
'sql_query' => $_POST['sql_query'],
'db' => $_POST['db'] ?? $_GET['db'] ?? null,
];
$this->response->addHTML(MultiTableQuery::displayResults($params['sql_query'], $params['db']));
}
}

View file

@ -1,48 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\MultiTableQuery;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Query\Generator as QueryGenerator;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function rtrim;
final class TablesController 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
{
$params = [
'tables' => $_GET['tables'] ?? [],
'db' => $_GET['db'] ?? '',
];
$tablesListForQuery = '';
foreach ($params['tables'] as $table) {
$tablesListForQuery .= "'" . $this->dbi->escapeString($table) . "',";
}
$tablesListForQuery = rtrim($tablesListForQuery, ',');
$constrains = $this->dbi->fetchResult(
QueryGenerator::getInformationSchemaForeignKeyConstraintsRequest(
$this->dbi->escapeString($params['db']),
$tablesListForQuery
)
);
$this->response->addJSON(['foreignKeyConstrains' => $constrains]);
}
}

View file

@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\MultiTableQuery;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
/**
* Handles database multi-table querying
*/
class MultiTableQueryController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
$this->addScriptFiles([
'database/multi_table_query.js',
'database/query_generator.js',
]);
$queryInstance = new MultiTableQuery($this->dbi, $this->template, $this->db);
$this->response->addHTML($queryInstance->getFormHtml());
}
}

View file

@ -1,104 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Operations;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\Operations;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
final class CollationController extends AbstractController
{
/** @var Operations */
private $operations;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Operations $operations,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->operations = $operations;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $cfg, $errorUrl;
if (! $this->response->isAjax()) {
return;
}
if (empty($_POST['db_collation'])) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', Message::error(__('No collation provided.')));
return;
}
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$sql_query = 'ALTER DATABASE ' . Util::backquote($db)
. ' DEFAULT' . Util::getCharsetQueryPart($_POST['db_collation'] ?? '');
$this->dbi->query($sql_query);
$message = Message::success();
/**
* Changes tables charset if requested by the user
*/
if (isset($_POST['change_all_tables_collations']) && $_POST['change_all_tables_collations'] === 'on') {
[$tables] = Util::getDbInfo($db, '');
foreach ($tables as $tableName => $data) {
if ($this->dbi->getTable($db, $tableName)->isView()) {
// Skip views, we can not change the collation of a view.
// issue #15283
continue;
}
$sql_query = 'ALTER TABLE '
. Util::backquote($db)
. '.'
. Util::backquote($tableName)
. ' DEFAULT '
. Util::getCharsetQueryPart($_POST['db_collation'] ?? '');
$this->dbi->query($sql_query);
/**
* Changes columns charset if requested by the user
*/
if (
! isset($_POST['change_all_tables_columns_collations']) ||
$_POST['change_all_tables_columns_collations'] !== 'on'
) {
continue;
}
$this->operations->changeAllColumnsCollation($db, $tableName, $_POST['db_collation']);
}
}
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
}
}

View file

@ -1,323 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Message;
use PhpMyAdmin\Operations;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Plugins\Export\ExportSql;
use PhpMyAdmin\Query\Utilities;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function count;
use function mb_strtolower;
use function strlen;
/**
* Handles miscellaneous database operations.
*/
class OperationsController extends AbstractController
{
/** @var Operations */
private $operations;
/** @var CheckUserPrivileges */
private $checkUserPrivileges;
/** @var Relation */
private $relation;
/** @var RelationCleanup */
private $relationCleanup;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Operations $operations,
CheckUserPrivileges $checkUserPrivileges,
Relation $relation,
RelationCleanup $relationCleanup,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->operations = $operations;
$this->checkUserPrivileges = $checkUserPrivileges;
$this->relation = $relation;
$this->relationCleanup = $relationCleanup;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $db, $server, $sql_query, $move, $message, $tables_full, $errorUrl;
global $export_sql_plugin, $views, $sqlConstratints, $local_query, $reload, $urlParams, $tables;
global $total_num_tables, $sub_part, $tooltip_truename;
global $db_collation, $tooltip_aliasname, $pos, $is_information_schema, $single_table, $num_tables;
$this->checkUserPrivileges->getPrivileges();
$this->addScriptFiles(['database/operations.js']);
$sql_query = '';
/**
* Rename/move or copy database
*/
if (strlen($db) > 0 && (! empty($_POST['db_rename']) || ! empty($_POST['db_copy']))) {
if (! empty($_POST['db_rename'])) {
$move = true;
} else {
$move = false;
}
if (! isset($_POST['newname']) || strlen($_POST['newname']) === 0) {
$message = Message::error(__('The database name is empty!'));
} else {
// lower_case_table_names=1 `DB` becomes `db`
if ($this->dbi->getLowerCaseNames() === '1') {
$_POST['newname'] = mb_strtolower($_POST['newname']);
}
if ($_POST['newname'] === $_REQUEST['db']) {
$message = Message::error(
__('Cannot copy database to the same name. Change the name and try again.')
);
} else {
$_error = false;
if ($move || ! empty($_POST['create_database_before_copying'])) {
$this->operations->createDbBeforeCopy();
}
// here I don't use DELIMITER because it's not part of the
// language; I have to send each statement one by one
// to avoid selecting alternatively the current and new db
// we would need to modify the CREATE definitions to qualify
// the db name
$this->operations->runProcedureAndFunctionDefinitions($db);
// go back to current db, just in case
$this->dbi->selectDb($db);
$tables_full = $this->dbi->getTablesFull($db);
// remove all foreign key constraints, otherwise we can get errors
/** @var ExportSql $export_sql_plugin */
$export_sql_plugin = Plugins::getPlugin('export', 'sql', [
'export_type' => 'database',
'single_table' => isset($single_table),
]);
// create stand-in tables for views
$views = $this->operations->getViewsAndCreateSqlViewStandIn($tables_full, $export_sql_plugin, $db);
// copy tables
$sqlConstratints = $this->operations->copyTables($tables_full, $move, $db);
// handle the views
if (! $_error) {
$this->operations->handleTheViews($views, $move, $db);
}
unset($views);
// now that all tables exist, create all the accumulated constraints
if (! $_error && count($sqlConstratints) > 0) {
$this->operations->createAllAccumulatedConstraints($sqlConstratints);
}
unset($sqlConstratints);
if ($this->dbi->getVersion() >= 50100) {
// here DELIMITER is not used because it's not part of the
// language; each statement is sent one by one
$this->operations->runEventDefinitionsForDb($db);
}
// go back to current db, just in case
$this->dbi->selectDb($db);
// Duplicate the bookmarks for this db (done once for each db)
$this->operations->duplicateBookmarks($_error, $db);
if (! $_error && $move) {
if (isset($_POST['adjust_privileges']) && ! empty($_POST['adjust_privileges'])) {
$this->operations->adjustPrivilegesMoveDb($db, $_POST['newname']);
}
/**
* cleanup pmadb stuff for this db
*/
$this->relationCleanup->database($db);
// if someday the RENAME DATABASE reappears, do not DROP
$local_query = 'DROP DATABASE '
. Util::backquote($db) . ';';
$sql_query .= "\n" . $local_query;
$this->dbi->query($local_query);
$message = Message::success(
__('Database %1$s has been renamed to %2$s.')
);
$message->addParam($db);
$message->addParam($_POST['newname']);
} elseif (! $_error) {
if (isset($_POST['adjust_privileges']) && ! empty($_POST['adjust_privileges'])) {
$this->operations->adjustPrivilegesCopyDb($db, $_POST['newname']);
}
$message = Message::success(
__('Database %1$s has been copied to %2$s.')
);
$message->addParam($db);
$message->addParam($_POST['newname']);
} else {
$message = Message::error();
}
$reload = true;
/* Change database to be used */
if (! $_error && $move) {
$db = $_POST['newname'];
} elseif (! $_error) {
if (isset($_POST['switch_to_new']) && $_POST['switch_to_new'] === 'true') {
$_SESSION['pma_switch_to_new'] = true;
$db = $_POST['newname'];
} else {
$_SESSION['pma_switch_to_new'] = false;
}
}
}
}
/**
* Database has been successfully renamed/moved. If in an Ajax request,
* generate the output with {@link ResponseRenderer} and exit
*/
if ($this->response->isAjax()) {
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
$this->response->addJSON('newname', $_POST['newname']);
$this->response->addJSON(
'sql_query',
Generator::getMessage('', $sql_query)
);
$this->response->addJSON('db', $db);
return;
}
}
$relationParameters = $this->relation->getRelationParameters();
/**
* Check if comments were updated
* (must be done before displaying the menu tabs)
*/
if (isset($_POST['comment'])) {
$this->relation->setDbComment($db, $_POST['comment']);
}
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$urlParams['goto'] = Url::getFromRoute('/database/operations');
// Gets the database structure
$sub_part = '_structure';
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,
$isSystemSchema,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part);
$oldMessage = '';
if (isset($message)) {
$oldMessage = Generator::getMessage($message, $sql_query);
unset($message);
}
$db_collation = $this->dbi->getDbCollation($db);
$is_information_schema = Utilities::isSystemSchema($db);
if ($is_information_schema) {
return;
}
$databaseComment = '';
if ($relationParameters->columnCommentsFeature !== null) {
$databaseComment = $this->relation->getDbComment($db);
}
$hasAdjustPrivileges = $GLOBALS['db_priv'] && $GLOBALS['table_priv']
&& $GLOBALS['col_priv'] && $GLOBALS['proc_priv'] && $GLOBALS['is_reload_priv'];
$isDropDatabaseAllowed = ($this->dbi->isSuperUser() || $cfg['AllowUserDropDatabase'])
&& ! $isSystemSchema && $db !== 'mysql';
$switchToNew = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new'];
$charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
$collations = Charsets::getCollations($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
if (! $relationParameters->hasAllFeatures() && $cfg['PmaNoRelation_DisableWarning'] == false) {
$message = Message::notice(
__(
'The phpMyAdmin configuration storage has been deactivated. %sFind out why%s.'
)
);
$message->addParamHtml(
'<a href="' . Url::getFromRoute('/check-relations')
. '" data-post="' . Url::getCommon(['db' => $db]) . '">'
);
$message->addParamHtml('</a>');
/* Show error if user has configured something, notice elsewhere */
if (! empty($cfg['Servers'][$server]['pmadb'])) {
$message->isError(true);
}
}
$this->render('database/operations/index', [
'message' => $oldMessage,
'db' => $db,
'has_comment' => $relationParameters->columnCommentsFeature !== null,
'db_comment' => $databaseComment,
'db_collation' => $db_collation,
'has_adjust_privileges' => $hasAdjustPrivileges,
'is_drop_database_allowed' => $isDropDatabaseAllowed,
'switch_to_new' => $switchToNew,
'charsets' => $charsets,
'collations' => $collations,
]);
}
}

View file

@ -1,71 +0,0 @@
<?php
/**
* Controller for database privileges
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Privileges;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function mb_strtolower;
/**
* Controller for database privileges
*/
class PrivilegesController extends AbstractController
{
/** @var Privileges */
private $privileges;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Privileges $privileges,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->privileges = $privileges;
$this->dbi = $dbi;
}
/**
* @param string[] $params Request parameters
* @psalm-param array{checkprivsdb: string} $params
*/
public function __invoke(array $params): string
{
global $cfg, $text_dir;
$scriptName = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$db = $params['checkprivsdb'];
if ($this->dbi->getLowerCaseNames() === '1') {
$db = mb_strtolower($params['checkprivsdb']);
}
$privileges = [];
if ($this->dbi->isSuperUser()) {
$privileges = $this->privileges->getAllPrivileges($db);
}
return $this->template->render('database/privileges/index', [
'is_superuser' => $this->dbi->isSuperUser(),
'db' => $db,
'database_url' => $scriptName,
'text_dir' => $text_dir,
'is_createuser' => $this->dbi->isCreateUser(),
'is_grantuser' => $this->dbi->isGrantUser(),
'privileges' => $privileges,
]);
}
}

View file

@ -1,168 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\Database\Qbe;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Operations;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\SavedSearches;
use PhpMyAdmin\Sql;
use PhpMyAdmin\Template;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function stripos;
class QueryByExampleController extends AbstractController
{
/** @var Relation */
private $relation;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Relation $relation,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->relation = $relation;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $savedSearchList, $savedSearch, $currentSearchId;
global $sql_query, $goto, $sub_part, $tables, $num_tables, $total_num_tables;
global $tooltip_truename, $tooltip_aliasname, $pos, $urlParams, $cfg, $errorUrl;
$savedQbeSearchesFeature = $this->relation->getRelationParameters()->savedQueryByExampleSearchesFeature;
$savedSearchList = [];
$savedSearch = null;
$currentSearchId = null;
$this->addScriptFiles(['database/qbe.js']);
if ($savedQbeSearchesFeature !== null) {
//Get saved search list.
$savedSearch = new SavedSearches();
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
->setDbname($db);
if (! empty($_POST['searchId'])) {
$savedSearch->setId($_POST['searchId']);
}
//Action field is sent.
if (isset($_POST['action'])) {
$savedSearch->setSearchName($_POST['searchName']);
if ($_POST['action'] === 'create') {
$savedSearch->setId(null)
->setCriterias($_POST)
->save($savedQbeSearchesFeature);
} elseif ($_POST['action'] === 'update') {
$savedSearch->setCriterias($_POST)
->save($savedQbeSearchesFeature);
} elseif ($_POST['action'] === 'delete') {
$savedSearch->delete($savedQbeSearchesFeature);
//After deletion, reset search.
$savedSearch = new SavedSearches();
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
->setDbname($db);
$_POST = [];
} elseif ($_POST['action'] === 'load') {
if (empty($_POST['searchId'])) {
//when not loading a search, reset the object.
$savedSearch = new SavedSearches();
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
->setDbname($db);
$_POST = [];
} else {
$savedSearch->load($savedQbeSearchesFeature);
}
}
//Else, it's an "update query"
}
$savedSearchList = $savedSearch->getList($savedQbeSearchesFeature);
$currentSearchId = $savedSearch->getId();
}
/**
* A query has been submitted -> (maybe) execute it
*/
$hasMessageToDisplay = false;
if (isset($_POST['submit_sql']) && ! empty($sql_query)) {
if (stripos($sql_query, 'SELECT') !== 0) {
$hasMessageToDisplay = true;
} else {
$goto = Url::getFromRoute('/database/sql');
$sql = new Sql(
$this->dbi,
$this->relation,
new RelationCleanup($this->dbi, $this->relation),
new Operations($this->dbi, $this->relation),
new Transformations(),
$this->template
);
$this->response->addHTML($sql->executeQueryAndSendQueryResponse(
null, // analyzed_sql_results
false, // is_gotofile
$_POST['db'], // db
null, // table
false, // find_real_end
null, // sql_query_for_bookmark
null, // extra_data
null, // message_to_show
null, // sql_data
$goto, // goto
null, // disp_query
null, // disp_message
$sql_query, // sql_query
null // complete_query
));
}
}
$sub_part = '_qbe';
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$urlParams['goto'] = Url::getFromRoute('/database/qbe');
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part);
$databaseQbe = new Qbe($this->relation, $this->template, $this->dbi, $db, $savedSearchList, $savedSearch);
$this->render('database/qbe/index', [
'url_params' => $urlParams,
'has_message_to_display' => $hasMessageToDisplay,
'selection_form_html' => $databaseQbe->getSelectionForm(),
]);
}
}

View file

@ -1,124 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\Database\Routines;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function in_array;
use function strlen;
/**
* Routines management.
*/
class RoutinesController extends AbstractController
{
/** @var CheckUserPrivileges */
private $checkUserPrivileges;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
CheckUserPrivileges $checkUserPrivileges,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->checkUserPrivileges = $checkUserPrivileges;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $table, $tables, $num_tables, $total_num_tables, $sub_part;
global $tooltip_truename, $tooltip_aliasname, $pos;
global $errors, $errorUrl, $urlParams, $cfg;
$this->addScriptFiles(['database/routines.js']);
$type = $_REQUEST['type'] ?? null;
$this->checkUserPrivileges->getPrivileges();
if (! $this->response->isAjax()) {
/**
* Displays the header and tabs
*/
if (! empty($table) && in_array($table, $this->dbi->getTables($db))) {
Util::checkParameters(['db', 'table']);
$urlParams = ['db' => $db, 'table' => $table];
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
$errorUrl .= Url::getCommon($urlParams, '&');
DbTableExists::check();
} else {
$table = '';
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
}
} elseif (strlen($db) > 0) {
$this->dbi->selectDb($db);
}
/**
* Keep a list of errors that occurred while
* processing an 'Add' or 'Edit' operation.
*/
$errors = [];
$routines = new Routines($this->dbi, $this->template, $this->response);
$routines->handleEditor();
$routines->handleExecute();
$routines->export();
if (! isset($type) || ! in_array($type, ['FUNCTION', 'PROCEDURE'])) {
$type = null;
}
$items = $this->dbi->getRoutines($db, $type);
$isAjax = $this->response->isAjax() && empty($_REQUEST['ajax_page_request']);
$rows = '';
foreach ($items as $item) {
$rows .= $routines->getRow($item, $isAjax ? 'ajaxInsert hide' : '');
}
$this->render('database/routines/index', [
'db' => $db,
'table' => $table,
'items' => $items,
'rows' => $rows,
'has_privilege' => Util::currentUserHasPrivilege('CREATE ROUTINE', $db, $table),
]);
}
}

View file

@ -1,85 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\Search;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
class SearchController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $db, $errorUrl, $urlParams, $tables, $num_tables, $total_num_tables, $sub_part;
global $tooltip_truename, $tooltip_aliasname, $pos;
$this->addScriptFiles(['database/search.js', 'sql.js', 'makegrid.js']);
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
// If config variable $cfg['UseDbSearch'] is on false : exit.
if (! $cfg['UseDbSearch']) {
Generator::mysqlDie(
__('Access denied!'),
'',
false,
$errorUrl
);
}
$urlParams['goto'] = Url::getFromRoute('/database/search');
// Create a database search instance
$databaseSearch = new Search($this->dbi, $db, $this->template);
// Display top links if we are not in an Ajax request
if (! $this->response->isAjax()) {
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
}
// Main search form has been submitted, get results
if (isset($_POST['submit_search'])) {
$this->response->addHTML($databaseSearch->getSearchResults());
}
// If we are in an Ajax request, we need to exit after displaying all the HTML
if ($this->response->isAjax() && empty($_REQUEST['ajax_page_request'])) {
return;
}
// Display the search form
$this->response->addHTML($databaseSearch->getMainHtml());
}
}

View file

@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function json_encode;
/**
* Table/Column autocomplete in SQL editors.
*/
class SqlAutoCompleteController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $db, $sql_autocomplete;
$sql_autocomplete = true;
if ($cfg['EnableAutocompleteForTablesAndColumns']) {
$db = $_POST['db'] ?? $db;
$sql_autocomplete = [];
if ($db) {
$tableNames = $this->dbi->getTables($db);
foreach ($tableNames as $tableName) {
$sql_autocomplete[$tableName] = $this->dbi->getColumns($db, $tableName);
}
}
}
$this->response->addJSON(['tables' => json_encode($sql_autocomplete)]);
}
}

View file

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\SqlQueryForm;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function htmlspecialchars;
/**
* Database SQL executor
*/
class SqlController extends AbstractController
{
/** @var SqlQueryForm */
private $sqlQueryForm;
public function __construct(ResponseRenderer $response, Template $template, string $db, SqlQueryForm $sqlQueryForm)
{
parent::__construct($response, $template, $db);
$this->sqlQueryForm = $sqlQueryForm;
}
public function __invoke(): void
{
global $goto, $back, $db, $cfg, $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());
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
/**
* After a syntax error, we return to this script
* with the typed query in the textarea.
*/
$goto = Url::getFromRoute('/database/sql');
$back = $goto;
$this->response->addHTML($this->sqlQueryForm->getHtml(
$db,
'',
true,
false,
isset($_POST['delimiter'])
? htmlspecialchars($_POST['delimiter'])
: ';'
));
}
}

View file

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\SqlParser\Utils\Formatter;
use function strlen;
/**
* Format SQL for SQL editors.
*/
class SqlFormatController extends AbstractController
{
public function __invoke(): void
{
$params = ['sql' => $_POST['sql'] ?? null];
$query = strlen((string) $params['sql']) > 0 ? $params['sql'] : '';
$this->response->addJSON(['sql' => Formatter::format($query)]);
}
}

View file

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use function __;
final class AddPrefixController extends AbstractController
{
public function __invoke(): void
{
global $db;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$params = ['db' => $db];
foreach ($selected as $selectedValue) {
$params['selected'][] = $selectedValue;
}
$this->response->disable();
$this->render('database/structure/add_prefix', ['url_params' => $params]);
}
}

View file

@ -1,64 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function count;
final class AddPrefixTableController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $message, $sql_query;
$selected = $_POST['selected'] ?? [];
$sql_query = '';
$selectedCount = count($selected);
for ($i = 0; $i < $selectedCount; $i++) {
$newTableName = $_POST['add_prefix'] . $selected[$i];
$aQuery = 'ALTER TABLE ' . Util::backquote($selected[$i])
. ' RENAME ' . Util::backquote($newTableName);
$sql_query .= $aQuery . ';' . "\n";
$this->dbi->selectDb($db);
$this->dbi->query($aQuery);
}
$message = Message::success();
if (empty($_POST['message'])) {
$_POST['message'] = $message;
}
($this->structureController)();
}
}

View file

@ -1,59 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure\CentralColumns;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\Database\CentralColumns;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class AddController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $message;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$centralColumns = new CentralColumns($this->dbi);
$error = $centralColumns->syncUniqueColumns($selected);
$message = $error instanceof Message ? $error : Message::success(__('Success!'));
unset($_POST['submit_mult']);
($this->structureController)();
}
}

View file

@ -1,59 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure\CentralColumns;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\Database\CentralColumns;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class MakeConsistentController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $message;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$centralColumns = new CentralColumns($this->dbi);
$error = $centralColumns->makeConsistentWithList($db, $selected);
$message = $error instanceof Message ? $error : Message::success(__('Success!'));
unset($_POST['submit_mult']);
($this->structureController)();
}
}

View file

@ -1,59 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure\CentralColumns;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\Database\CentralColumns;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class RemoveController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $message;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$centralColumns = new CentralColumns($this->dbi);
$error = $centralColumns->deleteColumnsFromList($_POST['db'], $selected);
$message = $error instanceof Message ? $error : Message::success(__('Success!'));
unset($_POST['submit_mult']);
($this->structureController)();
}
}

View file

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use function __;
final class ChangePrefixFormController extends AbstractController
{
public function __invoke(): void
{
global $db;
$selected = $_POST['selected_tbl'] ?? [];
$submitMult = $_POST['submit_mult'] ?? '';
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$route = '/database/structure/replace-prefix';
if ($submitMult === 'copy_tbl_change_prefix') {
$route = '/database/structure/copy-table-with-prefix';
}
$urlParams = ['db' => $db];
foreach ($selected as $selectedValue) {
$urlParams['selected'][] = $selectedValue;
}
$this->response->disable();
$this->render('database/structure/change_prefix_form', [
'route' => $route,
'url_params' => $urlParams,
]);
}
}

View file

@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use function __;
final class CopyFormController extends AbstractController
{
public function __invoke(): void
{
global $db, $dblist;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$urlParams = ['db' => $db];
foreach ($selected as $selectedValue) {
$urlParams['selected'][] = $selectedValue;
}
$databasesList = $dblist->databases;
foreach ($databasesList as $key => $databaseName) {
if ($databaseName == $db) {
$databasesList->offsetUnset($key);
break;
}
}
$this->response->disable();
$this->render('database/structure/copy_form', [
'url_params' => $urlParams,
'options' => $databasesList->getList(),
]);
}
}

View file

@ -1,72 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\Message;
use PhpMyAdmin\Operations;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Table;
use PhpMyAdmin\Template;
use function count;
final class CopyTableController extends AbstractController
{
/** @var Operations */
private $operations;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Operations $operations,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->operations = $operations;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $message;
$selected = $_POST['selected'] ?? [];
$targetDb = $_POST['target_db'] ?? null;
$selectedCount = count($selected);
for ($i = 0; $i < $selectedCount; $i++) {
Table::moveCopy(
$db,
$selected[$i],
$targetDb,
$selected[$i],
$_POST['what'],
false,
'one_table',
isset($_POST['drop_if_exists']) && $_POST['drop_if_exists'] === 'true'
);
if (empty($_POST['adjust_privileges'])) {
continue;
}
$this->operations->adjustPrivilegesCopyTable($db, $selected[$i], $targetDb, $selected[$i]);
}
$message = Message::success();
if (empty($_POST['message'])) {
$_POST['message'] = $message;
}
($this->structureController)();
}
}

View file

@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Table;
use PhpMyAdmin\Template;
use function count;
use function mb_strlen;
use function mb_substr;
final class CopyTableWithPrefixController extends AbstractController
{
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $message;
$selected = $_POST['selected'] ?? [];
$fromPrefix = $_POST['from_prefix'] ?? null;
$toPrefix = $_POST['to_prefix'] ?? null;
$selectedCount = count($selected);
for ($i = 0; $i < $selectedCount; $i++) {
$current = $selected[$i];
$newTableName = $toPrefix . mb_substr($current, mb_strlen((string) $fromPrefix));
Table::moveCopy(
$db,
$current,
$db,
$newTableName,
'data',
false,
'one_table',
isset($_POST['drop_if_exists']) && $_POST['drop_if_exists'] === 'true'
);
}
$message = Message::success();
if (empty($_POST['message'])) {
$_POST['message'] = $message;
}
($this->structureController)();
}
}

View file

@ -1,81 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use PhpMyAdmin\Utils\ForeignKey;
use function __;
use function htmlspecialchars;
use function in_array;
final class DropFormController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$views = $this->dbi->getVirtualTables($db);
$fullQueryViews = '';
$fullQuery = '';
foreach ($selected as $selectedValue) {
$current = $selectedValue;
if (! empty($views) && in_array($current, $views)) {
$fullQueryViews .= (empty($fullQueryViews) ? 'DROP VIEW ' : ', ')
. Util::backquote(htmlspecialchars($current));
} else {
$fullQuery .= (empty($fullQuery) ? 'DROP TABLE ' : ', ')
. Util::backquote(htmlspecialchars($current));
}
}
if (! empty($fullQuery)) {
$fullQuery .= ';<br>' . "\n";
}
if (! empty($fullQueryViews)) {
$fullQuery .= $fullQueryViews . ';<br>' . "\n";
}
$urlParams = ['db' => $db];
foreach ($selected as $selectedValue) {
$urlParams['selected'][] = $selectedValue;
}
foreach ($views as $current) {
$urlParams['views'][] = $current;
}
$this->render('database/structure/drop_form', [
'url_params' => $urlParams,
'full_query' => $fullQuery,
'is_foreign_key_check' => ForeignKey::isCheckEnabled(),
]);
}
}

View file

@ -1,135 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use PhpMyAdmin\Utils\ForeignKey;
use function __;
use function count;
use function in_array;
final class DropTableController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var RelationCleanup */
private $relationCleanup;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
RelationCleanup $relationCleanup,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->relationCleanup = $relationCleanup;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $message, $reload, $sql_query;
$reload = $_POST['reload'] ?? $reload ?? null;
$multBtn = $_POST['mult_btn'] ?? '';
$selected = $_POST['selected'] ?? [];
$views = $this->dbi->getVirtualTables($db);
if ($multBtn !== __('Yes')) {
$message = Message::success(__('No change'));
if (empty($_POST['message'])) {
$_POST['message'] = Message::success();
}
unset($_POST['mult_btn']);
($this->structureController)();
return;
}
$defaultFkCheckValue = ForeignKey::handleDisableCheckInit();
$sql_query = '';
$sqlQueryViews = '';
$selectedCount = count($selected);
for ($i = 0; $i < $selectedCount; $i++) {
$this->relationCleanup->table($db, $selected[$i]);
$current = $selected[$i];
if (! empty($views) && in_array($current, $views)) {
$sqlQueryViews .= (empty($sqlQueryViews) ? 'DROP VIEW ' : ', ') . Util::backquote($current);
} else {
$sql_query .= (empty($sql_query) ? 'DROP TABLE ' : ', ') . Util::backquote($current);
}
$reload = 1;
}
if (! empty($sql_query)) {
$sql_query .= ';';
} elseif (! empty($sqlQueryViews)) {
$sql_query = $sqlQueryViews . ';';
unset($sqlQueryViews);
}
// Unset cache values for tables count, issue #14205
if (isset($_SESSION['tmpval'])) {
if (isset($_SESSION['tmpval']['table_limit_offset'])) {
unset($_SESSION['tmpval']['table_limit_offset']);
}
if (isset($_SESSION['tmpval']['table_limit_offset_db'])) {
unset($_SESSION['tmpval']['table_limit_offset_db']);
}
}
$message = Message::success();
$this->dbi->selectDb($db);
$result = $this->dbi->tryQuery($sql_query);
if (! $result) {
$message = Message::error($this->dbi->getError());
}
if ($result && ! empty($sqlQueryViews)) {
$sql_query .= ' ' . $sqlQueryViews . ';';
$result = $this->dbi->tryQuery($sqlQueryViews);
unset($sqlQueryViews);
}
if (! $result) {
$message = Message::error($this->dbi->getError());
}
ForeignKey::handleDisableCheckCleanup($defaultFkCheckValue);
if (empty($_POST['message'])) {
$_POST['message'] = $message;
}
unset($_POST['mult_btn']);
($this->structureController)();
}
}

View file

@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Util;
use PhpMyAdmin\Utils\ForeignKey;
use function __;
use function htmlspecialchars;
final class EmptyFormController extends AbstractController
{
public function __invoke(): void
{
global $db;
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$fullQuery = '';
$urlParams = ['db' => $db];
foreach ($selected as $selectedValue) {
$fullQuery .= 'TRUNCATE ';
$fullQuery .= Util::backquote(htmlspecialchars($selectedValue)) . ';<br>';
$urlParams['selected'][] = $selectedValue;
}
$this->render('database/structure/empty_form', [
'url_params' => $urlParams,
'full_query' => $fullQuery,
'is_foreign_key_check' => ForeignKey::isCheckEnabled(),
]);
}
}

View file

@ -1,118 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\FlashMessages;
use PhpMyAdmin\Message;
use PhpMyAdmin\Operations;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Sql;
use PhpMyAdmin\Template;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Util;
use PhpMyAdmin\Utils\ForeignKey;
use function __;
use function count;
final class EmptyTableController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var Relation */
private $relation;
/** @var RelationCleanup */
private $relationCleanup;
/** @var Operations */
private $operations;
/** @var FlashMessages */
private $flash;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
Relation $relation,
RelationCleanup $relationCleanup,
Operations $operations,
FlashMessages $flash,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->relation = $relation;
$this->relationCleanup = $relationCleanup;
$this->operations = $operations;
$this->flash = $flash;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $table, $message, $sql_query;
$multBtn = $_POST['mult_btn'] ?? '';
$selected = $_POST['selected'] ?? [];
if ($multBtn !== __('Yes')) {
$this->flash->addMessage('success', __('No change'));
$this->redirect('/database/structure', ['db' => $db]);
return;
}
$defaultFkCheckValue = ForeignKey::handleDisableCheckInit();
$sql_query = '';
$selectedCount = count($selected);
for ($i = 0; $i < $selectedCount; $i++) {
$aQuery = 'TRUNCATE ';
$aQuery .= Util::backquote($selected[$i]);
$sql_query .= $aQuery . ';' . "\n";
$this->dbi->selectDb($db);
$this->dbi->query($aQuery);
}
if (! empty($_REQUEST['pos'])) {
$sql = new Sql(
$this->dbi,
$this->relation,
$this->relationCleanup,
$this->operations,
new Transformations(),
$this->template
);
$_REQUEST['pos'] = $sql->calculatePosForLastPage($db, $table, $_REQUEST['pos']);
}
ForeignKey::handleDisableCheckCleanup($defaultFkCheckValue);
$message = Message::success();
if (empty($_POST['message'])) {
$_POST['message'] = $message;
}
unset($_POST['mult_btn']);
($this->structureController)();
}
}

View file

@ -1,184 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\RecentFavoriteTable;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function count;
use function json_decode;
use function json_encode;
use function md5;
use function sha1;
final class FavoriteTableController extends AbstractController
{
/** @var Relation */
private $relation;
public function __construct(ResponseRenderer $response, Template $template, string $db, Relation $relation)
{
parent::__construct($response, $template, $db);
$this->relation = $relation;
}
public function __invoke(): void
{
global $cfg, $db, $errorUrl;
$parameters = [
'favorite_table' => $_REQUEST['favorite_table'] ?? null,
'favoriteTables' => $_REQUEST['favoriteTables'] ?? null,
'sync_favorite_tables' => $_REQUEST['sync_favorite_tables'] ?? null,
];
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase() || ! $this->response->isAjax()) {
return;
}
$favoriteInstance = RecentFavoriteTable::getInstance('favorite');
if (isset($parameters['favoriteTables'])) {
$favoriteTables = json_decode($parameters['favoriteTables'], true);
} else {
$favoriteTables = [];
}
// Required to keep each user's preferences separate.
$user = sha1($cfg['Server']['user']);
// Request for Synchronization of favorite tables.
if (isset($parameters['sync_favorite_tables'])) {
$relationParameters = $this->relation->getRelationParameters();
if ($relationParameters->favoriteTablesFeature !== null) {
$this->response->addJSON($this->synchronizeFavoriteTables(
$favoriteInstance,
$user,
$favoriteTables
));
}
return;
}
$changes = true;
$favoriteTable = $parameters['favorite_table'] ?? '';
$alreadyFavorite = $this->checkFavoriteTable($favoriteTable);
if (isset($_REQUEST['remove_favorite'])) {
if ($alreadyFavorite) {
// If already in favorite list, remove it.
$favoriteInstance->remove($this->db, $favoriteTable);
$alreadyFavorite = false; // for favorite_anchor template
}
} elseif (isset($_REQUEST['add_favorite'])) {
if (! $alreadyFavorite) {
$numTables = count($favoriteInstance->getTables());
if ($numTables == $cfg['NumFavoriteTables']) {
$changes = false;
} else {
// Otherwise add to favorite list.
$favoriteInstance->add($this->db, $favoriteTable);
$alreadyFavorite = true; // for favorite_anchor template
}
}
}
$favoriteTables[$user] = $favoriteInstance->getTables();
$json = [];
$json['changes'] = $changes;
if (! $changes) {
$json['message'] = $this->template->render('components/error_message', [
'msg' => __('Favorite List is full!'),
]);
$this->response->addJSON($json);
return;
}
// Check if current table is already in favorite list.
$favoriteParams = [
'db' => $this->db,
'ajax_request' => true,
'favorite_table' => $favoriteTable,
($alreadyFavorite ? 'remove' : 'add') . '_favorite' => true,
];
$json['user'] = $user;
$json['favoriteTables'] = json_encode($favoriteTables);
$json['list'] = $favoriteInstance->getHtmlList();
$json['anchor'] = $this->template->render('database/structure/favorite_anchor', [
'table_name_hash' => md5($favoriteTable),
'db_table_name_hash' => md5($this->db . '.' . $favoriteTable),
'fav_params' => $favoriteParams,
'already_favorite' => $alreadyFavorite,
]);
$this->response->addJSON($json);
}
/**
* Synchronize favorite tables
*
* @param RecentFavoriteTable $favoriteInstance Instance of this class
* @param string $user The user hash
* @param array $favoriteTables Existing favorites
*
* @return array
*/
private function synchronizeFavoriteTables(
RecentFavoriteTable $favoriteInstance,
string $user,
array $favoriteTables
): array {
$favoriteInstanceTables = $favoriteInstance->getTables();
if (empty($favoriteInstanceTables) && isset($favoriteTables[$user])) {
foreach ($favoriteTables[$user] as $value) {
$favoriteInstance->add($value['db'], $value['table']);
}
}
$favoriteTables[$user] = $favoriteInstance->getTables();
// Set flag when localStorage and pmadb(if present) are in sync.
$_SESSION['tmpval']['favorites_synced'][$GLOBALS['server']] = true;
return [
'favoriteTables' => json_encode($favoriteTables),
'list' => $favoriteInstance->getHtmlList(),
];
}
/**
* Function to check if a table is already in favorite list.
*
* @param string $currentTable current table
*/
private function checkFavoriteTable(string $currentTable): bool
{
// ensure $_SESSION['tmpval']['favoriteTables'] is initialized
RecentFavoriteTable::getInstance('favorite');
$favoriteTables = $_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']] ?? [];
foreach ($favoriteTables as $value) {
if ($value['db'] == $this->db && $value['table'] == $currentTable) {
return true;
}
}
return false;
}
}

View file

@ -1,79 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function json_encode;
/**
* Handles request for real row count on database level view page.
*/
final class RealRowCountController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $db, $errorUrl;
$parameters = [
'real_row_count_all' => $_REQUEST['real_row_count_all'] ?? null,
'table' => $_REQUEST['table'] ?? null,
];
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase() || ! $this->response->isAjax()) {
return;
}
[$tables] = Util::getDbInfo($this->db, '_structure');
// If there is a request to update all table's row count.
if (! isset($parameters['real_row_count_all'])) {
// Get the real row count for the table.
$realRowCount = (int) $this->dbi
->getTable($this->db, (string) $parameters['table'])
->getRealRowCountTable();
// Format the number.
$realRowCount = Util::formatNumber($realRowCount, 0);
$this->response->addJSON(['real_row_count' => $realRowCount]);
return;
}
// Array to store the results.
$realRowCountAll = [];
// Iterate over each table and fetch real row count.
foreach ($tables as $table) {
$rowCount = $this->dbi
->getTable($this->db, $table['TABLE_NAME'])
->getRealRowCountTable();
$realRowCountAll[] = [
'table' => $table['TABLE_NAME'],
'row_count' => $rowCount,
];
}
$this->response->addJSON(['real_row_count_all' => json_encode($realRowCountAll)]);
}
}

View file

@ -1,76 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Controllers\Database\StructureController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function count;
use function mb_strlen;
use function mb_substr;
final class ReplacePrefixController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
/** @var StructureController */
private $structureController;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
DatabaseInterface $dbi,
StructureController $structureController
) {
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
$this->structureController = $structureController;
}
public function __invoke(): void
{
global $db, $message, $sql_query;
$selected = $_POST['selected'] ?? [];
$fromPrefix = $_POST['from_prefix'] ?? '';
$toPrefix = $_POST['to_prefix'] ?? '';
$sql_query = '';
$selectedCount = count($selected);
for ($i = 0; $i < $selectedCount; $i++) {
$current = $selected[$i];
$subFromPrefix = mb_substr($current, 0, mb_strlen((string) $fromPrefix));
if ($subFromPrefix === $fromPrefix) {
$newTableName = $toPrefix . mb_substr($current, mb_strlen((string) $fromPrefix));
} else {
$newTableName = $current;
}
$aQuery = 'ALTER TABLE ' . Util::backquote($selected[$i])
. ' RENAME ' . Util::backquote($newTableName);
$sql_query .= $aQuery . ';' . "\n";
$this->dbi->selectDb($db);
$this->dbi->query($aQuery);
}
$message = Message::success();
if (empty($_POST['message'])) {
$_POST['message'] = $message;
}
($this->structureController)();
}
}

View file

@ -1,64 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\Database\AbstractController;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class ShowCreateController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
$selected = $_POST['selected_tbl'] ?? [];
if (empty($selected)) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
$tables = $this->getShowCreateTables($selected);
$showCreate = $this->template->render('database/structure/show_create', ['tables' => $tables]);
$this->response->addJSON('message', $showCreate);
}
/**
* @param string[] $selected Selected tables.
*
* @return array<string, array<int, array<string, string>>>
*/
private function getShowCreateTables(array $selected): array
{
$tables = ['tables' => [], 'views' => []];
foreach ($selected as $table) {
$object = $this->dbi->getTable($this->db, $table);
$tables[$object->isView() ? 'views' : 'tables'][] = [
'name' => Core::mimeDefaultFunction($table),
'show_create' => Core::mimeDefaultFunction($object->showCreate()),
];
}
return $tables;
}
}

View file

@ -1,918 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\RelationCleanup;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\FlashMessages;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Operations;
use PhpMyAdmin\RecentFavoriteTable;
use PhpMyAdmin\Replication;
use PhpMyAdmin\ReplicationInfo;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\StorageEngine;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tracker;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function array_search;
use function ceil;
use function count;
use function htmlspecialchars;
use function implode;
use function in_array;
use function is_string;
use function max;
use function mb_substr;
use function md5;
use function preg_match;
use function preg_quote;
use function sprintf;
use function str_replace;
use function strlen;
use function strtotime;
use function urlencode;
/**
* Handles database structure logic
*/
class StructureController extends AbstractController
{
/** @var int Number of tables */
protected $numTables;
/** @var int Current position in the list */
protected $position;
/** @var bool DB is information_schema */
protected $dbIsSystemSchema;
/** @var int Number of tables */
protected $totalNumTables;
/** @var array Tables in the database */
protected $tables;
/** @var bool whether stats show or not */
protected $isShowStats;
/** @var Relation */
private $relation;
/** @var Replication */
private $replication;
/** @var RelationCleanup */
private $relationCleanup;
/** @var Operations */
private $operations;
/** @var ReplicationInfo */
private $replicationInfo;
/** @var DatabaseInterface */
private $dbi;
/** @var FlashMessages */
private $flash;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Relation $relation,
Replication $replication,
RelationCleanup $relationCleanup,
Operations $operations,
DatabaseInterface $dbi,
FlashMessages $flash
) {
parent::__construct($response, $template, $db);
$this->relation = $relation;
$this->replication = $replication;
$this->relationCleanup = $relationCleanup;
$this->operations = $operations;
$this->dbi = $dbi;
$this->flash = $flash;
$this->replicationInfo = new ReplicationInfo($this->dbi);
}
/**
* Retrieves database information for further use
*
* @param string $subPart Page part name
*/
private function getDatabaseInfo(string $subPart): void
{
[
$tables,
$numTables,
$totalNumTables,,
$isShowStats,
$dbIsSystemSchema,,,
$position,
] = Util::getDbInfo($this->db, $subPart);
$this->tables = $tables;
$this->numTables = $numTables;
$this->position = $position;
$this->dbIsSystemSchema = $dbIsSystemSchema;
$this->totalNumTables = $totalNumTables;
$this->isShowStats = $isShowStats;
}
public function __invoke(): void
{
global $cfg, $db, $errorUrl;
$parameters = [
'sort' => $_REQUEST['sort'] ?? null,
'sort_order' => $_REQUEST['sort_order'] ?? null,
];
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$this->addScriptFiles(['database/structure.js', 'table/change.js']);
// Gets the database structure
$this->getDatabaseInfo('_structure');
// Checks if there are any tables to be shown on current page.
// If there are no tables, the user is redirected to the last page
// having any.
if ($this->totalNumTables > 0 && $this->position > $this->totalNumTables) {
$this->redirect('/database/structure', [
'db' => $this->db,
'pos' => max(0, $this->totalNumTables - $cfg['MaxTableList']),
'reload' => 1,
]);
}
$this->replicationInfo->load($_POST['primary_connection'] ?? null);
$replicaInfo = $this->replicationInfo->getReplicaInfo();
$pageSettings = new PageSettings('DbStructure');
$this->response->addHTML($pageSettings->getErrorHTML());
$this->response->addHTML($pageSettings->getHTML());
if ($this->numTables > 0) {
$urlParams = [
'pos' => $this->position,
'db' => $this->db,
];
if (isset($parameters['sort'])) {
$urlParams['sort'] = $parameters['sort'];
}
if (isset($parameters['sort_order'])) {
$urlParams['sort_order'] = $parameters['sort_order'];
}
$listNavigator = Generator::getListNavigator(
$this->totalNumTables,
$this->position,
$urlParams,
Url::getFromRoute('/database/structure'),
'frame_content',
$cfg['MaxTableList']
);
$tableList = $this->displayTableList($replicaInfo);
}
$createTable = '';
if (empty($this->dbIsSystemSchema)) {
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
$checkUserPrivileges->getPrivileges();
$createTable = $this->template->render('database/create_table', ['db' => $this->db]);
}
$this->render('database/structure/index', [
'database' => $this->db,
'has_tables' => $this->numTables > 0,
'list_navigator_html' => $listNavigator ?? '',
'table_list_html' => $tableList ?? '',
'is_system_schema' => ! empty($this->dbIsSystemSchema),
'create_table_html' => $createTable,
]);
}
/**
* @param array $replicaInfo
*/
protected function displayTableList($replicaInfo): string
{
$html = '';
// filtering
$html .= $this->template->render('filter', ['filter_value' => '']);
$i = $sumEntries = 0;
$overheadCheck = false;
$createTimeAll = '';
$updateTimeAll = '';
$checkTimeAll = '';
$numColumns = $GLOBALS['cfg']['PropertiesNumColumns'] > 1
? ceil($this->numTables / $GLOBALS['cfg']['PropertiesNumColumns']) + 1
: 0;
$rowCount = 0;
$sumSize = 0;
$overheadSize = 0;
$hiddenFields = [];
$overallApproxRows = false;
$structureTableRows = [];
foreach ($this->tables as $currentTable) {
// Get valid statistics whatever is the table type
$dropQuery = '';
$dropMessage = '';
$overhead = '';
$inputClass = ['checkall'];
// Sets parameters for links
$tableUrlParams = [
'db' => $this->db,
'table' => $currentTable['TABLE_NAME'],
];
// do not list the previous table's size info for a view
[
$currentTable,
$formattedSize,
$unit,
$formattedOverhead,
$overheadUnit,
$overheadSize,
$tableIsView,
$sumSize,
] = $this->getStuffForEngineTypeTable($currentTable, $sumSize, $overheadSize);
$curTable = $this->dbi
->getTable($this->db, $currentTable['TABLE_NAME']);
if (! $curTable->isMerge()) {
$sumEntries += $currentTable['TABLE_ROWS'];
}
$collationDefinition = '---';
if (isset($currentTable['Collation'])) {
$tableCollation = Charsets::findCollationByName(
$this->dbi,
$GLOBALS['cfg']['Server']['DisableIS'],
$currentTable['Collation']
);
if ($tableCollation !== null) {
$collationDefinition = $this->template->render('database/structure/collation_definition', [
'valueTitle' => $tableCollation->getDescription(),
'value' => $tableCollation->getName(),
]);
}
}
if ($this->isShowStats) {
$overhead = '-';
if ($formattedOverhead != '') {
$overhead = $this->template->render('database/structure/overhead', [
'table_url_params' => $tableUrlParams,
'formatted_overhead' => $formattedOverhead,
'overhead_unit' => $overheadUnit,
]);
$overheadCheck = true;
$inputClass[] = 'tbl-overhead';
}
}
if ($GLOBALS['cfg']['ShowDbStructureCharset']) {
$charset = '';
if (isset($tableCollation)) {
$charset = $tableCollation->getCharset();
}
}
if ($GLOBALS['cfg']['ShowDbStructureCreation']) {
$createTime = $currentTable['Create_time'] ?? '';
if ($createTime && (! $createTimeAll || $createTime < $createTimeAll)) {
$createTimeAll = $createTime;
}
}
if ($GLOBALS['cfg']['ShowDbStructureLastUpdate']) {
$updateTime = $currentTable['Update_time'] ?? '';
if ($updateTime && (! $updateTimeAll || $updateTime < $updateTimeAll)) {
$updateTimeAll = $updateTime;
}
}
if ($GLOBALS['cfg']['ShowDbStructureLastCheck']) {
$checkTime = $currentTable['Check_time'] ?? '';
if ($checkTime && (! $checkTimeAll || $checkTime < $checkTimeAll)) {
$checkTimeAll = $checkTime;
}
}
$truename = $currentTable['TABLE_NAME'];
$i++;
$rowCount++;
if ($tableIsView) {
$hiddenFields[] = '<input type="hidden" name="views[]" value="'
. htmlspecialchars($currentTable['TABLE_NAME']) . '">';
}
/*
* Always activate links for Browse, Search and Empty, even if
* the icons are greyed, because
* 1. for views, we don't know the number of rows at this point
* 2. for tables, another source could have populated them since the
* page was generated
*
* I could have used the PHP ternary conditional operator but I find
* the code easier to read without this operator.
*/
$mayHaveRows = $currentTable['TABLE_ROWS'] > 0 || $tableIsView;
if (! $this->dbIsSystemSchema) {
$dropQuery = sprintf(
'DROP %s %s',
$tableIsView || $currentTable['ENGINE'] == null ? 'VIEW'
: 'TABLE',
Util::backquote(
$currentTable['TABLE_NAME']
)
);
$dropMessage = sprintf(
($tableIsView || $currentTable['ENGINE'] == null
? __('View %s has been dropped.')
: __('Table %s has been dropped.')),
str_replace(
' ',
'&nbsp;',
htmlspecialchars($currentTable['TABLE_NAME'])
)
);
}
if ($numColumns > 0 && $this->numTables > $numColumns && ($rowCount % $numColumns) == 0) {
$rowCount = 1;
$html .= $this->template->render('database/structure/table_header', [
'db' => $this->db,
'db_is_system_schema' => $this->dbIsSystemSchema,
'replication' => $replicaInfo['status'],
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
'is_show_stats' => $this->isShowStats,
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
'structure_table_rows' => $structureTableRows,
]);
$structureTableRows = [];
}
[$approxRows, $showSuperscript] = $this->isRowCountApproximated($currentTable, $tableIsView);
[$do, $ignored] = $this->getReplicationStatus($replicaInfo, $truename);
$structureTableRows[] = [
'table_name_hash' => md5($currentTable['TABLE_NAME']),
'db_table_name_hash' => md5($this->db . '.' . $currentTable['TABLE_NAME']),
'db' => $this->db,
'curr' => $i,
'input_class' => implode(' ', $inputClass),
'table_is_view' => $tableIsView,
'current_table' => $currentTable,
'may_have_rows' => $mayHaveRows,
'browse_table_label_title' => htmlspecialchars($currentTable['TABLE_COMMENT']),
'browse_table_label_truename' => $truename,
'empty_table_sql_query' => 'TRUNCATE ' . Util::backquote($currentTable['TABLE_NAME']),
'empty_table_message_to_show' => urlencode(
sprintf(
__('Table %s has been emptied.'),
htmlspecialchars(
$currentTable['TABLE_NAME']
)
)
),
'tracking_icon' => $this->getTrackingIcon($truename),
'server_replica_status' => $replicaInfo['status'],
'table_url_params' => $tableUrlParams,
'db_is_system_schema' => $this->dbIsSystemSchema,
'drop_query' => $dropQuery,
'drop_message' => $dropMessage,
'collation' => $collationDefinition,
'formatted_size' => $formattedSize,
'unit' => $unit,
'overhead' => $overhead,
'create_time' => isset($createTime) && $createTime
? Util::localisedDate(strtotime($createTime)) : '-',
'update_time' => isset($updateTime) && $updateTime
? Util::localisedDate(strtotime($updateTime)) : '-',
'check_time' => isset($checkTime) && $checkTime
? Util::localisedDate(strtotime($checkTime)) : '-',
'charset' => $charset ?? '',
'is_show_stats' => $this->isShowStats,
'ignored' => $ignored,
'do' => $do,
'approx_rows' => $approxRows,
'show_superscript' => $showSuperscript,
'already_favorite' => $this->checkFavoriteTable($currentTable['TABLE_NAME']),
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
'limit_chars' => $GLOBALS['cfg']['LimitChars'],
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
];
$overallApproxRows = $overallApproxRows || $approxRows;
}
$databaseCollation = [];
$databaseCharset = '';
$collation = Charsets::findCollationByName(
$this->dbi,
$GLOBALS['cfg']['Server']['DisableIS'],
$this->dbi->getDbCollation($this->db)
);
if ($collation !== null) {
$databaseCollation = [
'name' => $collation->getName(),
'description' => $collation->getDescription(),
];
$databaseCharset = $collation->getCharset();
}
$relationParameters = $this->relation->getRelationParameters();
return $html . $this->template->render('database/structure/table_header', [
'db' => $this->db,
'db_is_system_schema' => $this->dbIsSystemSchema,
'replication' => $replicaInfo['status'],
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
'is_show_stats' => $this->isShowStats,
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
'structure_table_rows' => $structureTableRows,
'body_for_table_summary' => [
'num_tables' => $this->numTables,
'server_replica_status' => $replicaInfo['status'],
'db_is_system_schema' => $this->dbIsSystemSchema,
'sum_entries' => $sumEntries,
'database_collation' => $databaseCollation,
'is_show_stats' => $this->isShowStats,
'database_charset' => $databaseCharset,
'sum_size' => $sumSize,
'overhead_size' => $overheadSize,
'create_time_all' => $createTimeAll ? Util::localisedDate(strtotime($createTimeAll)) : '-',
'update_time_all' => $updateTimeAll ? Util::localisedDate(strtotime($updateTimeAll)) : '-',
'check_time_all' => $checkTimeAll ? Util::localisedDate(strtotime($checkTimeAll)) : '-',
'approx_rows' => $overallApproxRows,
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
'db' => $GLOBALS['db'],
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
'dbi' => $this->dbi,
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
],
'check_all_tables' => [
'text_dir' => $GLOBALS['text_dir'],
'overhead_check' => $overheadCheck,
'db_is_system_schema' => $this->dbIsSystemSchema,
'hidden_fields' => $hiddenFields,
'disable_multi_table' => $GLOBALS['cfg']['DisableMultiTableMaintenance'],
'central_columns_work' => $relationParameters->centralColumnsFeature !== null,
],
]);
}
/**
* Returns the tracking icon if the table is tracked
*
* @param string $table table name
*
* @return string HTML for tracking icon
*/
protected function getTrackingIcon(string $table): string
{
$trackingIcon = '';
if (Tracker::isActive()) {
$isTracked = Tracker::isTracked($this->db, $table);
if ($isTracked || Tracker::getVersion($this->db, $table) > 0) {
$trackingIcon = $this->template->render('database/structure/tracking_icon', [
'db' => $this->db,
'table' => $table,
'is_tracked' => $isTracked,
]);
}
}
return $trackingIcon;
}
/**
* Returns whether the row count is approximated
*
* @param array $currentTable array containing details about the table
* @param bool $tableIsView whether the table is a view
*
* @return array
*/
protected function isRowCountApproximated(
array $currentTable,
bool $tableIsView
): array {
$approxRows = false;
$showSuperscript = '';
// there is a null value in the ENGINE
// - when the table needs to be repaired, or
// - when it's a view
// so ensure that we'll display "in use" below for a table
// that needs to be repaired
if (isset($currentTable['TABLE_ROWS']) && ($currentTable['ENGINE'] != null || $tableIsView)) {
// InnoDB/TokuDB table: we did not get an accurate row count
$approxRows = ! $tableIsView
&& in_array($currentTable['ENGINE'], ['InnoDB', 'TokuDB'])
&& ! $currentTable['COUNTED'];
if ($tableIsView && $currentTable['TABLE_ROWS'] >= $GLOBALS['cfg']['MaxExactCountViews']) {
$approxRows = true;
$showSuperscript = Generator::showHint(
Sanitize::sanitizeMessage(
sprintf(
__(
'This view has at least this number of rows. Please refer to %sdocumentation%s.'
),
'[doc@cfg_MaxExactCountViews]',
'[/doc]'
)
)
);
}
}
return [
$approxRows,
$showSuperscript,
];
}
/**
* Returns the replication status of the table.
*
* @param array $replicaInfo
* @param string $table table name
*
* @return array
*/
protected function getReplicationStatus($replicaInfo, string $table): array
{
$do = $ignored = false;
if ($replicaInfo['status']) {
$nbServReplicaDoDb = count($replicaInfo['Do_DB']);
$nbServReplicaIgnoreDb = count($replicaInfo['Ignore_DB']);
$searchDoDBInTruename = array_search($table, $replicaInfo['Do_DB']);
$searchDoDBInDB = array_search($this->db, $replicaInfo['Do_DB']);
$do = (is_string($searchDoDBInTruename) && strlen($searchDoDBInTruename) > 0)
|| (is_string($searchDoDBInDB) && strlen($searchDoDBInDB) > 0)
|| ($nbServReplicaDoDb == 0 && $nbServReplicaIgnoreDb == 0)
|| $this->hasTable($replicaInfo['Wild_Do_Table'], $table);
$searchDb = array_search($this->db, $replicaInfo['Ignore_DB']);
$searchTable = array_search($table, $replicaInfo['Ignore_Table']);
$ignored = (is_string($searchTable) && strlen($searchTable) > 0)
|| (is_string($searchDb) && strlen($searchDb) > 0)
|| $this->hasTable($replicaInfo['Wild_Ignore_Table'], $table);
}
return [
$do,
$ignored,
];
}
/**
* Function to check if a table is already in favorite list.
*
* @param string $currentTable current table
*/
protected function checkFavoriteTable(string $currentTable): bool
{
// ensure $_SESSION['tmpval']['favoriteTables'] is initialized
RecentFavoriteTable::getInstance('favorite');
$favoriteTables = $_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']] ?? [];
foreach ($favoriteTables as $value) {
if ($value['db'] == $this->db && $value['table'] == $currentTable) {
return true;
}
}
return false;
}
/**
* Find table with truename
*
* @param array $db DB to look into
* @param string $truename Table name
*/
protected function hasTable(array $db, $truename): bool
{
foreach ($db as $dbTable) {
if (
$this->db == $this->replication->extractDbOrTable($dbTable)
&& preg_match(
'@^' .
preg_quote(mb_substr($this->replication->extractDbOrTable($dbTable, 'table'), 0, -1), '@') . '@',
$truename
)
) {
return true;
}
}
return false;
}
/**
* Get the value set for ENGINE table,
*
* @internal param bool $table_is_view whether table is view or not
*
* @param array $currentTable current table
* @param int $sumSize total table size
* @param int $overheadSize overhead size
*
* @return array
*/
protected function getStuffForEngineTypeTable(
array $currentTable,
$sumSize,
$overheadSize
) {
$formattedSize = '-';
$unit = '';
$formattedOverhead = '';
$overheadUnit = '';
$tableIsView = false;
switch ($currentTable['ENGINE']) {
// MyISAM, ISAM or Heap table: Row count, data size and index size
// are accurate; data size is accurate for ARCHIVE
case 'MyISAM':
case 'ISAM':
case 'HEAP':
case 'MEMORY':
case 'ARCHIVE':
case 'Aria':
case 'Maria':
[
$currentTable,
$formattedSize,
$unit,
$formattedOverhead,
$overheadUnit,
$overheadSize,
$sumSize,
] = $this->getValuesForAriaTable(
$currentTable,
$sumSize,
$overheadSize,
$formattedSize,
$unit,
$formattedOverhead,
$overheadUnit
);
break;
case 'InnoDB':
case 'PBMS':
case 'TokuDB':
// InnoDB table: Row count is not accurate but data and index sizes are.
// PBMS table in Drizzle: TABLE_ROWS is taken from table cache,
// so it may be unavailable
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForInnodbTable(
$currentTable,
$sumSize
);
break;
// Mysql 5.0.x (and lower) uses MRG_MyISAM
// and MySQL 5.1.x (and higher) uses MRG_MYISAM
// Both are aliases for MERGE
case 'MRG_MyISAM':
case 'MRG_MYISAM':
case 'MERGE':
case 'BerkeleyDB':
// Merge or BerkleyDB table: Only row count is accurate.
if ($this->isShowStats) {
$formattedSize = ' - ';
$unit = '';
}
break;
// for a view, the ENGINE is sometimes reported as null,
// or on some servers it's reported as "SYSTEM VIEW"
case null:
case 'SYSTEM VIEW':
// possibly a view, do nothing
break;
case 'Mroonga':
// The idea is to show the size only if Mroonga is available,
// in other case the old unknown message will appear
if (StorageEngine::hasMroongaEngine()) {
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForMroongaTable(
$currentTable,
$sumSize
);
break;
}
// no break, go to default case
default:
// Unknown table type.
if ($this->isShowStats) {
$formattedSize = __('unknown');
$unit = '';
}
}
if ($currentTable['TABLE_TYPE'] === 'VIEW' || $currentTable['TABLE_TYPE'] === 'SYSTEM VIEW') {
// countRecords() takes care of $cfg['MaxExactCountViews']
$currentTable['TABLE_ROWS'] = $this->dbi
->getTable($this->db, $currentTable['TABLE_NAME'])
->countRecords(true);
$tableIsView = true;
}
return [
$currentTable,
$formattedSize,
$unit,
$formattedOverhead,
$overheadUnit,
$overheadSize,
$tableIsView,
$sumSize,
];
}
/**
* Get values for ARIA/MARIA tables
*
* @param array $currentTable current table
* @param int $sumSize sum size
* @param int $overheadSize overhead size
* @param int $formattedSize formatted size
* @param string $unit unit
* @param int $formattedOverhead overhead formatted
* @param string $overheadUnit overhead unit
*
* @return array
*/
protected function getValuesForAriaTable(
array $currentTable,
$sumSize,
$overheadSize,
$formattedSize,
$unit,
$formattedOverhead,
$overheadUnit
) {
if ($this->dbIsSystemSchema) {
$currentTable['Rows'] = $this->dbi
->getTable($this->db, $currentTable['Name'])
->countRecords();
}
if ($this->isShowStats) {
/** @var int $tblsize */
$tblsize = $currentTable['Data_length']
+ $currentTable['Index_length'];
$sumSize += $tblsize;
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, $tblsize > 0 ? 1 : 0);
if (isset($currentTable['Data_free']) && $currentTable['Data_free'] > 0) {
[$formattedOverhead, $overheadUnit] = Util::formatByteDown(
$currentTable['Data_free'],
3,
($currentTable['Data_free'] > 0 ? 1 : 0)
);
$overheadSize += $currentTable['Data_free'];
}
}
return [
$currentTable,
$formattedSize,
$unit,
$formattedOverhead,
$overheadUnit,
$overheadSize,
$sumSize,
];
}
/**
* Get values for InnoDB table
*
* @param array $currentTable current table
* @param int $sumSize sum size
*
* @return array
*/
protected function getValuesForInnodbTable(
array $currentTable,
$sumSize
) {
$formattedSize = $unit = '';
if (
(in_array($currentTable['ENGINE'], ['InnoDB', 'TokuDB'])
&& $currentTable['TABLE_ROWS'] < $GLOBALS['cfg']['MaxExactCount'])
|| ! isset($currentTable['TABLE_ROWS'])
) {
$currentTable['COUNTED'] = true;
$currentTable['TABLE_ROWS'] = $this->dbi
->getTable($this->db, $currentTable['TABLE_NAME'])
->countRecords(true);
} else {
$currentTable['COUNTED'] = false;
}
if ($this->isShowStats) {
/** @var int $tblsize */
$tblsize = $currentTable['Data_length']
+ $currentTable['Index_length'];
$sumSize += $tblsize;
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, ($tblsize > 0 ? 1 : 0));
}
return [
$currentTable,
$formattedSize,
$unit,
$sumSize,
];
}
/**
* Get values for Mroonga table
*
* @param array $currentTable current table
* @param int $sumSize sum size
*
* @return array
*/
protected function getValuesForMroongaTable(
array $currentTable,
int $sumSize
): array {
$formattedSize = '';
$unit = '';
if ($this->isShowStats) {
/** @var int $tblsize */
$tblsize = $currentTable['Data_length'] + $currentTable['Index_length'];
$sumSize += $tblsize;
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, ($tblsize > 0 ? 1 : 0));
}
return [
$currentTable,
$formattedSize,
$unit,
$sumSize,
];
}
}

View file

@ -1,157 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tracker;
use PhpMyAdmin\Tracking;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function count;
use function htmlspecialchars;
use function sprintf;
/**
* Tracking configuration for database.
*/
class TrackingController extends AbstractController
{
/** @var Tracking */
private $tracking;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
string $db,
Tracking $tracking,
DatabaseInterface $dbi
) {
parent::__construct($response, $template, $db);
$this->tracking = $tracking;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $text_dir, $urlParams, $tables, $num_tables;
global $total_num_tables, $sub_part, $pos, $data, $cfg;
global $tooltip_truename, $tooltip_aliasname, $errorUrl;
$this->addScriptFiles(['vendor/jquery/jquery.tablesorter.js', 'database/tracking.js']);
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
$urlParams['goto'] = Url::getFromRoute('/table/tracking');
$urlParams['back'] = Url::getFromRoute('/database/tracking');
// Get the database structure
$sub_part = '_structure';
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,
$isSystemSchema,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part);
if (isset($_POST['delete_tracking'], $_POST['table'])) {
Tracker::deleteTracking($db, $_POST['table']);
echo Message::success(
__('Tracking data deleted successfully.')
)->getDisplay();
} elseif (isset($_POST['submit_create_version'])) {
$this->tracking->createTrackingForMultipleTables($db, $_POST['selected']);
echo Message::success(
sprintf(
__(
'Version %1$s was created for selected tables, tracking is active for them.'
),
htmlspecialchars($_POST['version'])
)
)->getDisplay();
} elseif (isset($_POST['submit_mult'])) {
if (! empty($_POST['selected_tbl'])) {
if ($_POST['submit_mult'] === 'delete_tracking') {
foreach ($_POST['selected_tbl'] as $table) {
Tracker::deleteTracking($db, $table);
}
echo Message::success(
__('Tracking data deleted successfully.')
)->getDisplay();
} elseif ($_POST['submit_mult'] === 'track') {
echo $this->template->render('create_tracking_version', [
'route' => '/database/tracking',
'url_params' => $urlParams,
'last_version' => 0,
'db' => $db,
'selected' => $_POST['selected_tbl'],
'type' => 'both',
'default_statements' => $cfg['Server']['tracking_default_statements'],
]);
return;
}
} else {
echo Message::notice(
__('No tables selected.')
)->getDisplay();
}
}
// Get tracked data about the database
$data = Tracker::getTrackedData($db, '', '1');
// No tables present and no log exist
if ($num_tables == 0 && count($data['ddlog']) === 0) {
echo '<p>' , __('No tables found in database.') , '</p>' , "\n";
if (empty($isSystemSchema)) {
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
$checkUserPrivileges->getPrivileges();
echo $this->template->render('database/create_table', ['db' => $db]);
}
return;
}
echo $this->tracking->getHtmlForDbTrackingTables($db, $urlParams, $text_dir);
// If available print out database log
if (count($data['ddlog']) <= 0) {
return;
}
$log = '';
foreach ($data['ddlog'] as $entry) {
$log .= '# ' . $entry['date'] . ' ' . $entry['username'] . "\n"
. $entry['statement'] . "\n";
}
echo Generator::getMessage(__('Database Log'), $log);
}
}

View file

@ -1,87 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Database;
use PhpMyAdmin\Database\Triggers;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function in_array;
use function strlen;
/**
* Triggers management.
*/
class TriggersController extends AbstractController
{
/** @var DatabaseInterface */
private $dbi;
public function __construct(ResponseRenderer $response, Template $template, string $db, DatabaseInterface $dbi)
{
parent::__construct($response, $template, $db);
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $db, $table, $tables, $num_tables, $total_num_tables, $sub_part;
global $tooltip_truename, $tooltip_aliasname, $pos;
global $errors, $urlParams, $errorUrl, $cfg;
$this->addScriptFiles(['database/triggers.js']);
if (! $this->response->isAjax()) {
/**
* Displays the header and tabs
*/
if (! empty($table) && in_array($table, $this->dbi->getTables($db))) {
Util::checkParameters(['db', 'table']);
$urlParams = ['db' => $db, 'table' => $table];
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
$errorUrl .= Url::getCommon($urlParams, '&');
DbTableExists::check();
} else {
$table = '';
Util::checkParameters(['db']);
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
$errorUrl .= Url::getCommon(['db' => $db], '&');
if (! $this->hasDatabase()) {
return;
}
[
$tables,
$num_tables,
$total_num_tables,
$sub_part,,,
$tooltip_truename,
$tooltip_aliasname,
$pos,
] = Util::getDbInfo($db, $sub_part ?? '');
}
} elseif (strlen($db) > 0) {
$this->dbi->selectDb($db);
}
/**
* Keep a list of errors that occurred while
* processing an 'Add' or 'Edit' operation.
*/
$errors = [];
$triggers = new Triggers($this->dbi, $this->template, $this->response);
$triggers->main();
}
}

View file

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
final class DatabaseController extends AbstractController
{
public function __invoke(): void
{
global $dblist;
$this->response->addJSON(['databases' => $dblist->databases]);
}
}

View file

@ -1,164 +0,0 @@
<?php
/**
* Handle error report submission
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\ErrorHandler;
use PhpMyAdmin\ErrorReport;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\UserPreferences;
use function __;
use function count;
use function in_array;
use function is_string;
use function json_decode;
use function time;
/**
* Handle error report submission
*/
class ErrorReportController extends AbstractController
{
/** @var ErrorReport */
private $errorReport;
/** @var ErrorHandler */
private $errorHandler;
public function __construct(
ResponseRenderer $response,
Template $template,
ErrorReport $errorReport,
ErrorHandler $errorHandler
) {
parent::__construct($response, $template);
$this->errorReport = $errorReport;
$this->errorHandler = $errorHandler;
}
public function __invoke(ServerRequest $request): void
{
global $cfg;
/** @var string $exceptionType */
$exceptionType = $request->getParsedBodyParam('exception_type', '');
/** @var string|null $sendErrorReport */
$sendErrorReport = $request->getParsedBodyParam('send_error_report');
/** @var string|null $automatic */
$automatic = $request->getParsedBodyParam('automatic');
/** @var string|null $alwaysSend */
$alwaysSend = $request->getParsedBodyParam('always_send');
/** @var string|null $getSettings */
$getSettings = $request->getParsedBodyParam('get_settings');
if (! in_array($exceptionType, ['js', 'php'])) {
return;
}
if ($sendErrorReport) {
if ($exceptionType === 'php') {
/**
* Prevent infinite error submission.
* Happens in case error submissions fails.
* If reporting is done in some time interval,
* just clear them & clear json data too.
*/
if (
isset($_SESSION['prev_error_subm_time'], $_SESSION['error_subm_count'])
&& $_SESSION['error_subm_count'] >= 3
&& ($_SESSION['prev_error_subm_time'] - time()) <= 3000
) {
$_SESSION['error_subm_count'] = 0;
$_SESSION['prev_errors'] = '';
$this->response->addJSON('stopErrorReportLoop', '1');
} else {
$_SESSION['prev_error_subm_time'] = time();
$_SESSION['error_subm_count'] = isset($_SESSION['error_subm_count'])
? $_SESSION['error_subm_count'] + 1
: 0;
}
}
$reportData = $this->errorReport->getData($exceptionType);
// report if and only if there were 'actual' errors.
if (count($reportData) > 0) {
$server_response = $this->errorReport->send($reportData);
if (! is_string($server_response)) {
$success = false;
} else {
$decoded_response = json_decode($server_response, true);
$success = ! empty($decoded_response) ?
$decoded_response['success'] : false;
}
/* Message to show to the user */
if ($success) {
if ($automatic === 'true' || $cfg['SendErrorReports'] === 'always') {
$msg = __(
'An error has been detected and an error report has been '
. 'automatically submitted based on your settings.'
);
} else {
$msg = __('Thank you for submitting this report.');
}
} else {
$msg = __(
'An error has been detected and an error report has been generated but failed to be sent.'
);
$msg .= ' ';
$msg .= __('If you experience any problems please submit a bug report manually.');
}
$msg .= ' ' . __('You may want to refresh the page.');
/* Create message object */
if ($success) {
$msg = Message::notice($msg);
} else {
$msg = Message::error($msg);
}
/* Add message to response */
if ($this->response->isAjax()) {
if ($exceptionType === 'js') {
$this->response->addJSON('message', $msg);
} else {
$this->response->addJSON('errSubmitMsg', $msg);
}
} elseif ($exceptionType === 'php') {
$jsCode = 'Functions.ajaxShowMessage(\'<div class="alert alert-danger" role="alert">'
. $msg
. '</div>\', false);';
$this->response->getFooter()->getScripts()->addCode($jsCode);
}
if ($exceptionType === 'php') {
// clear previous errors & save new ones.
$this->errorHandler->savePreviousErrors();
}
/* Persist always send settings */
if ($alwaysSend === 'true') {
$userPreferences = new UserPreferences();
$userPreferences->persistOption('SendErrorReports', 'always', 'ask');
}
}
} elseif ($getSettings) {
$this->response->addJSON('report_setting', $cfg['SendErrorReports']);
} elseif ($exceptionType === 'js') {
$this->response->addJSON('report_modal', $this->errorReport->getEmptyModal());
$this->response->addHTML($this->errorReport->getForm());
} else {
// clear previous errors & save new ones.
$this->errorHandler->savePreviousErrors();
}
}
}

View file

@ -1,24 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export;
use PhpMyAdmin\Controllers\AbstractController;
final class CheckTimeOutController extends AbstractController
{
public function __invoke(): void
{
$this->response->setAjax(true);
if (isset($_SESSION['pma_export_error'])) {
unset($_SESSION['pma_export_error']);
$this->response->addJSON('message', 'timeout');
return;
}
$this->response->addJSON('message', 'success');
}
}

View file

@ -1,682 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Controllers\Database\ExportController as DatabaseExportController;
use PhpMyAdmin\Core;
use PhpMyAdmin\Encoding;
use PhpMyAdmin\Exceptions\ExportException;
use PhpMyAdmin\Export;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\SqlParser\Statements\SelectStatement;
use PhpMyAdmin\SqlParser\Utils\Misc;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function count;
use function function_exists;
use function in_array;
use function ini_set;
use function is_array;
use function ob_end_clean;
use function ob_get_length;
use function ob_get_level;
use function register_shutdown_function;
use function strlen;
use function time;
use const PHP_EOL;
final class ExportController extends AbstractController
{
/** @var Export */
private $export;
public function __construct(ResponseRenderer $response, Template $template, Export $export)
{
parent::__construct($response, $template);
$this->export = $export;
}
public function __invoke(ServerRequest $request): void
{
global $containerBuilder, $db, $export_type, $filename_template, $sql_query, $errorUrl, $message;
global $compression, $crlf, $asfile, $buffer_needed, $save_on_server, $file_handle, $separate_files;
global $output_charset_conversion, $output_kanji_conversion, $table, $what, $export_plugin, $single_table;
global $compression_methods, $onserver, $back_button, $refreshButton, $save_filename, $filename;
global $quick_export, $cfg, $tables, $table_select, $aliases;
global $time_start, $charset, $remember_template, $mime_type, $num_tables;
global $active_page, $do_relation, $do_comments, $do_mime, $do_dates, $whatStrucOrData, $db_select;
global $table_structure, $table_data, $lock_tables, $allrows, $limit_to, $limit_from;
/** @var array<string, string> $postParams */
$postParams = $request->getParsedBody();
/** @var string $whatParam */
$whatParam = $request->getParsedBodyParam('what', '');
/** @var string|null $quickOrCustom */
$quickOrCustom = $request->getParsedBodyParam('quick_or_custom');
/** @var string|null $outputFormat */
$outputFormat = $request->getParsedBodyParam('output_format');
/** @var string $compressionParam */
$compressionParam = $request->getParsedBodyParam('compression', '');
/** @var string|null $asSeparateFiles */
$asSeparateFiles = $request->getParsedBodyParam('as_separate_files');
/** @var string|null $quickExportOnServer */
$quickExportOnServer = $request->getParsedBodyParam('quick_export_onserver');
/** @var string|null $onServerParam */
$onServerParam = $request->getParsedBodyParam('onserver');
/** @var array|null $aliasesParam */
$aliasesParam = $request->getParsedBodyParam('aliases');
/** @var string|null $structureOrDataForced */
$structureOrDataForced = $request->getParsedBodyParam('structure_or_data_forced');
$this->addScriptFiles(['export_output.js']);
/**
* Sets globals from $_POST
*
* - Please keep the parameters in order of their appearance in the form
* - Some of these parameters are not used, as the code below directly
* verifies from the superglobal $_POST or $_REQUEST
* TODO: this should be removed to avoid passing user input to GLOBALS
* without checking
*/
$allowedPostParams = [
'db',
'table',
'what',
'single_table',
'export_type',
'export_method',
'quick_or_custom',
'db_select',
'table_select',
'table_structure',
'table_data',
'limit_to',
'limit_from',
'allrows',
'lock_tables',
'output_format',
'filename_template',
'maxsize',
'remember_template',
'charset',
'compression',
'as_separate_files',
'knjenc',
'xkana',
'htmlword_structure_or_data',
'htmlword_null',
'htmlword_columns',
'mediawiki_headers',
'mediawiki_structure_or_data',
'mediawiki_caption',
'pdf_structure_or_data',
'odt_structure_or_data',
'odt_relation',
'odt_comments',
'odt_mime',
'odt_columns',
'odt_null',
'codegen_structure_or_data',
'codegen_format',
'excel_null',
'excel_removeCRLF',
'excel_columns',
'excel_edition',
'excel_structure_or_data',
'yaml_structure_or_data',
'ods_null',
'ods_structure_or_data',
'ods_columns',
'json_structure_or_data',
'json_pretty_print',
'json_unicode',
'xml_structure_or_data',
'xml_export_events',
'xml_export_functions',
'xml_export_procedures',
'xml_export_tables',
'xml_export_triggers',
'xml_export_views',
'xml_export_contents',
'texytext_structure_or_data',
'texytext_columns',
'texytext_null',
'phparray_structure_or_data',
'sql_include_comments',
'sql_header_comment',
'sql_dates',
'sql_relation',
'sql_mime',
'sql_use_transaction',
'sql_disable_fk',
'sql_compatibility',
'sql_structure_or_data',
'sql_create_database',
'sql_drop_table',
'sql_procedure_function',
'sql_create_table',
'sql_create_view',
'sql_create_trigger',
'sql_view_current_user',
'sql_simple_view_export',
'sql_if_not_exists',
'sql_or_replace_view',
'sql_auto_increment',
'sql_backquotes',
'sql_truncate',
'sql_delayed',
'sql_ignore',
'sql_type',
'sql_insert_syntax',
'sql_max_query_size',
'sql_hex_for_binary',
'sql_utc_time',
'sql_drop_database',
'sql_views_as_tables',
'sql_metadata',
'csv_separator',
'csv_enclosed',
'csv_escaped',
'csv_terminated',
'csv_null',
'csv_removeCRLF',
'csv_columns',
'csv_structure_or_data',
// csv_replace should have been here but we use it directly from $_POST
'latex_caption',
'latex_structure_or_data',
'latex_structure_caption',
'latex_structure_continued_caption',
'latex_structure_label',
'latex_relation',
'latex_comments',
'latex_mime',
'latex_columns',
'latex_data_caption',
'latex_data_continued_caption',
'latex_data_label',
'latex_null',
'aliases',
];
foreach ($allowedPostParams as $param) {
if (! isset($postParams[$param])) {
continue;
}
$GLOBALS[$param] = $postParams[$param];
}
Util::checkParameters(['what', 'export_type']);
// sanitize this parameter which will be used below in a file inclusion
$what = Core::securePath($whatParam);
// export class instance, not array of properties, as before
/** @var ExportPlugin $export_plugin */
$export_plugin = Plugins::getPlugin('export', $what, [
'export_type' => (string) $export_type,
'single_table' => isset($single_table),
]);
// Check export type
if (empty($export_plugin)) {
Core::fatalError(__('Bad type!'));
}
/**
* valid compression methods
*/
$compression_methods = [];
if ($GLOBALS['cfg']['ZipDump'] && function_exists('gzcompress')) {
$compression_methods[] = 'zip';
}
if ($GLOBALS['cfg']['GZipDump'] && function_exists('gzencode')) {
$compression_methods[] = 'gzip';
}
/**
* init and variable checking
*/
$compression = '';
$onserver = false;
$save_on_server = false;
$buffer_needed = false;
$back_button = '';
$refreshButton = '';
$save_filename = '';
$file_handle = '';
$errorUrl = '';
$filename = '';
$separate_files = '';
// Is it a quick or custom export?
if ($quickOrCustom === 'quick') {
$quick_export = true;
} else {
$quick_export = false;
}
if ($outputFormat === 'astext') {
$asfile = false;
} else {
$asfile = true;
if ($asSeparateFiles && $compressionParam === 'zip') {
$separate_files = $asSeparateFiles;
}
if (in_array($compressionParam, $compression_methods)) {
$compression = $compressionParam;
$buffer_needed = true;
}
if (($quick_export && $quickExportOnServer) || (! $quick_export && $onServerParam)) {
if ($quick_export) {
$onserver = $quickExportOnServer;
} else {
$onserver = $onServerParam;
}
// Will we save dump on server?
$save_on_server = ! empty($cfg['SaveDir']);
}
}
/**
* If we are sending the export file (as opposed to just displaying it
* as text), we have to bypass the usual PhpMyAdmin\Response mechanism
*/
if ($outputFormat === 'sendit' && ! $save_on_server) {
$this->response->disable();
//Disable all active buffers (see: ob_get_status(true) at this point)
do {
if (ob_get_length() > 0 || ob_get_level() > 0) {
$hasBuffer = ob_end_clean();
} else {
$hasBuffer = false;
}
} while ($hasBuffer);
}
$tables = [];
// Generate error url and check for needed variables
if ($export_type === 'server') {
$errorUrl = Url::getFromRoute('/server/export');
} elseif ($export_type === 'database' && strlen($db) > 0) {
$errorUrl = Url::getFromRoute('/database/export', ['db' => $db]);
// Check if we have something to export
$tables = $table_select ?? [];
} elseif ($export_type === 'table' && strlen($db) > 0 && strlen($table) > 0) {
$errorUrl = Url::getFromRoute('/table/export', [
'db' => $db,
'table' => $table,
]);
} elseif ($export_type === 'raw') {
$errorUrl = Url::getFromRoute('/server/export', ['sql_query' => $sql_query]);
} else {
Core::fatalError(__('Bad parameters!'));
}
// Merge SQL Query aliases with Export aliases from
// export page, Export page aliases are given more
// preference over SQL Query aliases.
$parser = new Parser($sql_query);
$aliases = [];
if (! empty($parser->statements[0]) && ($parser->statements[0] instanceof SelectStatement)) {
$aliases = Misc::getAliases($parser->statements[0], $db);
}
if (! empty($aliasesParam)) {
$aliases = $this->export->mergeAliases($aliases, $aliasesParam);
$_SESSION['tmpval']['aliases'] = $aliasesParam;
}
/**
* Increase time limit for script execution and initializes some variables
*/
Util::setTimeLimit();
if (! empty($cfg['MemoryLimit'])) {
ini_set('memory_limit', $cfg['MemoryLimit']);
}
register_shutdown_function([$this->export, 'shutdown']);
// Start with empty buffer
$this->export->dumpBuffer = '';
$this->export->dumpBufferLength = 0;
// Array of dump buffers - used in separate file exports
$this->export->dumpBufferObjects = [];
// We send fake headers to avoid browser timeout when buffering
$time_start = time();
// Defines the default <CR><LF> format.
// For SQL always use \n as MySQL wants this on all platforms.
if ($what === 'sql') {
$crlf = "\n";
} else {
$crlf = PHP_EOL;
}
$output_kanji_conversion = Encoding::canConvertKanji();
// Do we need to convert charset?
$output_charset_conversion = $asfile
&& Encoding::isSupported()
&& isset($charset) && $charset !== 'utf-8';
// Use on the fly compression?
$GLOBALS['onfly_compression'] = $GLOBALS['cfg']['CompressOnFly']
&& $compression === 'gzip';
if ($GLOBALS['onfly_compression']) {
$GLOBALS['memory_limit'] = $this->export->getMemoryLimit();
}
// Generate filename and mime type if needed
if ($asfile) {
if (empty($remember_template)) {
$remember_template = '';
}
[$filename, $mime_type] = $this->export->getFilenameAndMimetype(
$export_type,
$remember_template,
$export_plugin,
$compression,
$filename_template
);
} else {
$mime_type = '';
}
// For raw query export, filename will be export.extension
if ($export_type === 'raw') {
[$filename] = $this->export->getFinalFilenameAndMimetypeForFilename($export_plugin, $compression, 'export');
}
// Open file on server if needed
if ($save_on_server) {
[$save_filename, $message, $file_handle] = $this->export->openFile($filename, $quick_export);
// problem opening export file on server?
if (! empty($message)) {
$this->export->showPage($export_type);
return;
}
} else {
/**
* Send headers depending on whether the user chose to download a dump file
* or not
*/
if ($asfile) {
// Download
// (avoid rewriting data containing HTML with anchors and forms;
// this was reported to happen under Plesk)
ini_set('url_rewriter.tags', '');
$filename = Sanitize::sanitizeFilename($filename);
Core::downloadHeader($filename, $mime_type);
} else {
// HTML
if ($export_type === 'database') {
$num_tables = count($tables);
if ($num_tables === 0) {
$message = Message::error(
__('No tables found in database.')
);
$active_page = Url::getFromRoute('/database/export');
/** @var DatabaseExportController $controller */
$controller = $containerBuilder->get(DatabaseExportController::class);
$controller();
exit;
}
}
[$html, $back_button, $refreshButton] = $this->export->getHtmlForDisplayedExportHeader(
$export_type,
$db,
$table
);
echo $html;
unset($html);
}
}
try {
// Re - initialize
$this->export->dumpBuffer = '';
$this->export->dumpBufferLength = 0;
// Add possibly some comments to export
if (! $export_plugin->exportHeader()) {
throw new ExportException('Failure during header export.');
}
// Will we need relation & co. setup?
$do_relation = isset($GLOBALS[$what . '_relation']);
$do_comments = isset($GLOBALS[$what . '_include_comments'])
|| isset($GLOBALS[$what . '_comments']);
$do_mime = isset($GLOBALS[$what . '_mime']);
// Include dates in export?
$do_dates = isset($GLOBALS[$what . '_dates']);
$whatStrucOrData = $GLOBALS[$what . '_structure_or_data'];
if ($export_type === 'raw') {
$whatStrucOrData = 'raw';
}
/**
* Builds the dump
*/
if ($export_type === 'server') {
if (! isset($db_select)) {
$db_select = '';
}
$this->export->exportServer(
$db_select,
$whatStrucOrData,
$export_plugin,
$crlf,
$errorUrl,
$export_type,
$do_relation,
$do_comments,
$do_mime,
$do_dates,
$aliases,
$separate_files
);
} elseif ($export_type === 'database') {
if (! isset($table_structure) || ! is_array($table_structure)) {
$table_structure = [];
}
if (! isset($table_data) || ! is_array($table_data)) {
$table_data = [];
}
if ($structureOrDataForced) {
$table_structure = $tables;
$table_data = $tables;
}
if (isset($lock_tables)) {
$this->export->lockTables($db, $tables, 'READ');
try {
$this->export->exportDatabase(
$db,
$tables,
$whatStrucOrData,
$table_structure,
$table_data,
$export_plugin,
$crlf,
$errorUrl,
$export_type,
$do_relation,
$do_comments,
$do_mime,
$do_dates,
$aliases,
$separate_files
);
} finally {
$this->export->unlockTables();
}
} else {
$this->export->exportDatabase(
$db,
$tables,
$whatStrucOrData,
$table_structure,
$table_data,
$export_plugin,
$crlf,
$errorUrl,
$export_type,
$do_relation,
$do_comments,
$do_mime,
$do_dates,
$aliases,
$separate_files
);
}
} elseif ($export_type === 'raw') {
Export::exportRaw($whatStrucOrData, $export_plugin, $crlf, $errorUrl, $db, $sql_query, $export_type);
} else {
// We export just one table
// $allrows comes from the form when "Dump all rows" has been selected
if (! isset($allrows)) {
$allrows = '';
}
if (! isset($limit_to)) {
$limit_to = '0';
}
if (! isset($limit_from)) {
$limit_from = '0';
}
if (isset($lock_tables)) {
try {
$this->export->lockTables($db, [$table], 'READ');
$this->export->exportTable(
$db,
$table,
$whatStrucOrData,
$export_plugin,
$crlf,
$errorUrl,
$export_type,
$do_relation,
$do_comments,
$do_mime,
$do_dates,
$allrows,
$limit_to,
$limit_from,
$sql_query,
$aliases
);
} finally {
$this->export->unlockTables();
}
} else {
$this->export->exportTable(
$db,
$table,
$whatStrucOrData,
$export_plugin,
$crlf,
$errorUrl,
$export_type,
$do_relation,
$do_comments,
$do_mime,
$do_dates,
$allrows,
$limit_to,
$limit_from,
$sql_query,
$aliases
);
}
}
if (! $export_plugin->exportFooter()) {
throw new ExportException('Failure during footer export.');
}
} catch (ExportException $e) {
// Ignore
}
if ($save_on_server && ! empty($message)) {
$this->export->showPage($export_type);
return;
}
/**
* Send the dump as a file...
*/
if (empty($asfile)) {
echo $this->export->getHtmlForDisplayedExportFooter($back_button, $refreshButton);
return;
}
// Convert the charset if required.
if ($output_charset_conversion) {
$this->export->dumpBuffer = Encoding::convertString(
'utf-8',
$GLOBALS['charset'],
$this->export->dumpBuffer
);
}
// Compression needed?
if ($compression) {
if (! empty($separate_files)) {
$this->export->dumpBuffer = $this->export->compress(
$this->export->dumpBufferObjects,
$compression,
$filename
);
} else {
$this->export->dumpBuffer = $this->export->compress($this->export->dumpBuffer, $compression, $filename);
}
}
/* If we saved on server, we have to close file now */
if ($save_on_server) {
$message = $this->export->closeFile($file_handle, $this->export->dumpBuffer, $save_filename);
$this->export->showPage($export_type);
return;
}
echo $this->export->dumpBuffer;
}
}

View file

@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Controllers\Database\ExportController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class TablesController extends AbstractController
{
/** @var ExportController */
private $exportController;
public function __construct(ResponseRenderer $response, Template $template, ExportController $exportController)
{
parent::__construct($response, $template);
$this->exportController = $exportController;
}
public function __invoke(): void
{
if (empty($_POST['selected_tbl'])) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', __('No table selected.'));
return;
}
($this->exportController)();
}
}

View file

@ -1,89 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export\Template;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Export\Template as ExportTemplate;
use PhpMyAdmin\Export\TemplateModel;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function is_array;
final class CreateController extends AbstractController
{
/** @var TemplateModel */
private $model;
/** @var Relation */
private $relation;
public function __construct(
ResponseRenderer $response,
Template $template,
TemplateModel $model,
Relation $relation
) {
parent::__construct($response, $template);
$this->model = $model;
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
global $cfg;
/** @var string $exportType */
$exportType = $request->getParsedBodyParam('exportType', '');
/** @var string $templateName */
$templateName = $request->getParsedBodyParam('templateName', '');
/** @var string $templateData */
$templateData = $request->getParsedBodyParam('templateData', '');
/** @var string|null $templateId */
$templateId = $request->getParsedBodyParam('template_id');
$exportTemplatesFeature = $this->relation->getRelationParameters()->exportTemplatesFeature;
if ($exportTemplatesFeature === null) {
return;
}
$template = ExportTemplate::fromArray([
'username' => $cfg['Server']['user'],
'exportType' => $exportType,
'name' => $templateName,
'data' => $templateData,
]);
$result = $this->model->create(
$exportTemplatesFeature->database,
$exportTemplatesFeature->exportTemplates,
$template
);
if ($result !== '') {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $result);
return;
}
$templates = $this->model->getAll(
$exportTemplatesFeature->database,
$exportTemplatesFeature->exportTemplates,
$template->getUsername(),
$template->getExportType()
);
$this->response->setRequestStatus(true);
$this->response->addJSON(
'data',
$this->template->render('export/template_options', [
'templates' => is_array($templates) ? $templates : [],
'selected_template' => $templateId,
])
);
}
}

View file

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export\Template;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Export\TemplateModel;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
final class DeleteController extends AbstractController
{
/** @var TemplateModel */
private $model;
/** @var Relation */
private $relation;
public function __construct(
ResponseRenderer $response,
Template $template,
TemplateModel $model,
Relation $relation
) {
parent::__construct($response, $template);
$this->model = $model;
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
global $cfg;
$templateId = (int) $request->getParsedBodyParam('templateId');
$exportTemplatesFeature = $this->relation->getRelationParameters()->exportTemplatesFeature;
if ($exportTemplatesFeature === null) {
return;
}
$result = $this->model->delete(
$exportTemplatesFeature->database,
$exportTemplatesFeature->exportTemplates,
$cfg['Server']['user'],
$templateId
);
if ($result !== '') {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $result);
return;
}
$this->response->setRequestStatus(true);
}
}

View file

@ -1,62 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export\Template;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Export\Template as ExportTemplate;
use PhpMyAdmin\Export\TemplateModel;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
final class LoadController extends AbstractController
{
/** @var TemplateModel */
private $model;
/** @var Relation */
private $relation;
public function __construct(
ResponseRenderer $response,
Template $template,
TemplateModel $model,
Relation $relation
) {
parent::__construct($response, $template);
$this->model = $model;
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
global $cfg;
$templateId = (int) $request->getParsedBodyParam('templateId');
$exportTemplatesFeature = $this->relation->getRelationParameters()->exportTemplatesFeature;
if ($exportTemplatesFeature === null) {
return;
}
$template = $this->model->load(
$exportTemplatesFeature->database,
$exportTemplatesFeature->exportTemplates,
$cfg['Server']['user'],
$templateId
);
if (! $template instanceof ExportTemplate) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $template);
return;
}
$this->response->setRequestStatus(true);
$this->response->addJSON('data', $template->getData());
}
}

View file

@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Export\Template;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Export\Template as ExportTemplate;
use PhpMyAdmin\Export\TemplateModel;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
final class UpdateController extends AbstractController
{
/** @var TemplateModel */
private $model;
/** @var Relation */
private $relation;
public function __construct(
ResponseRenderer $response,
Template $template,
TemplateModel $model,
Relation $relation
) {
parent::__construct($response, $template);
$this->model = $model;
$this->relation = $relation;
}
public function __invoke(ServerRequest $request): void
{
global $cfg;
$templateId = (int) $request->getParsedBodyParam('templateId');
/** @var string $templateData */
$templateData = $request->getParsedBodyParam('templateData', '');
$exportTemplatesFeature = $this->relation->getRelationParameters()->exportTemplatesFeature;
if ($exportTemplatesFeature === null) {
return;
}
$template = ExportTemplate::fromArray([
'id' => $templateId,
'username' => $cfg['Server']['user'],
'data' => $templateData,
]);
$result = $this->model->update(
$exportTemplatesFeature->database,
$exportTemplatesFeature->exportTemplates,
$template
);
if ($result !== '') {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $result);
return;
}
$this->response->setRequestStatus(true);
}
}

View file

@ -1,162 +0,0 @@
<?php
/**
* Editor for Geometry data types.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Gis\GisFactory;
use PhpMyAdmin\Gis\GisVisualization;
use PhpMyAdmin\Http\ServerRequest;
use function array_merge;
use function in_array;
use function intval;
use function is_array;
use function mb_strpos;
use function mb_strtoupper;
use function mb_substr;
use function substr;
use function trim;
/**
* Editor for Geometry data types.
*/
class GisDataEditorController extends AbstractController
{
public function __invoke(ServerRequest $request): void
{
global $gis_data, $gis_types, $start, $geom_type, $gis_obj, $srid, $wkt, $wkt_with_zero;
global $result, $visualizationSettings, $data, $visualization, $open_layers, $geom_count, $dbi;
/** @var string|null $field */
$field = $request->getParsedBodyParam('field');
/** @var array|null $gisDataParam */
$gisDataParam = $request->getParsedBodyParam('gis_data');
/** @var string $type */
$type = $request->getParsedBodyParam('type', '');
/** @var string|null $value */
$value = $request->getParsedBodyParam('value');
/** @var string|null $generate */
$generate = $request->getParsedBodyParam('generate');
/** @var string|null $inputName */
$inputName = $request->getParsedBodyParam('input_name');
if (! isset($field)) {
return;
}
// Get data if any posted
$gis_data = [];
if (is_array($gisDataParam)) {
$gis_data = $gisDataParam;
}
$gis_types = [
'POINT',
'MULTIPOINT',
'LINESTRING',
'MULTILINESTRING',
'POLYGON',
'MULTIPOLYGON',
'GEOMETRYCOLLECTION',
];
// Extract type from the initial call and make sure that it's a valid one.
// Extract from field's values if available, if not use the column type passed.
if (! isset($gis_data['gis_type'])) {
if ($type !== '') {
$gis_data['gis_type'] = mb_strtoupper($type);
}
if (isset($value) && trim($value) !== '') {
$start = substr($value, 0, 1) == "'" ? 1 : 0;
$gis_data['gis_type'] = mb_substr($value, $start, (int) mb_strpos($value, '(') - $start);
}
if (! isset($gis_data['gis_type']) || (! in_array($gis_data['gis_type'], $gis_types))) {
$gis_data['gis_type'] = $gis_types[0];
}
}
$geom_type = $gis_data['gis_type'];
// Generate parameters from value passed.
$gis_obj = GisFactory::factory($geom_type);
if ($gis_obj === false) {
return;
}
if (isset($value)) {
$gis_data = array_merge(
$gis_data,
$gis_obj->generateParams($value)
);
}
// Generate Well Known Text
$srid = isset($gis_data['srid']) && $gis_data['srid'] != '' ? (int) $gis_data['srid'] : 0;
$wkt = $gis_obj->generateWkt($gis_data, 0);
$wkt_with_zero = $gis_obj->generateWkt($gis_data, 0, '0');
$result = "'" . $wkt . "'," . $srid;
// Generate SVG based visualization
$visualizationSettings = [
'width' => 450,
'height' => 300,
'spatialColumn' => 'wkt',
'mysqlVersion' => $dbi->getVersion(),
'isMariaDB' => $dbi->isMariaDB(),
];
$data = [
[
'wkt' => $wkt_with_zero,
'srid' => $srid,
],
];
$visualization = GisVisualization::getByData($data, $visualizationSettings)
->toImage('svg');
$open_layers = GisVisualization::getByData($data, $visualizationSettings)
->asOl();
// If the call is to update the WKT and visualization make an AJAX response
if ($generate) {
$this->response->addJSON([
'result' => $result,
'visualization' => $visualization,
'openLayers' => $open_layers,
]);
return;
}
$geom_count = 1;
if ($geom_type === 'GEOMETRYCOLLECTION') {
$geom_count = isset($gis_data[$geom_type]['geom_count'])
? intval($gis_data[$geom_type]['geom_count']) : 1;
if (isset($gis_data[$geom_type]['add_geom'])) {
$geom_count++;
}
}
$templateOutput = $this->template->render('gis_data_editor_form', [
'width' => $visualizationSettings['width'],
'height' => $visualizationSettings['height'],
'field' => $field,
'input_name' => $inputName,
'srid' => $srid,
'visualization' => $visualization,
'open_layers' => $open_layers,
'gis_types' => $gis_types,
'geom_type' => $geom_type,
'geom_count' => $geom_count,
'gis_data' => $gis_data,
'result' => $result,
]);
$this->response->addJSON(['gis_editor' => $templateOutput]);
}
}

View file

@ -1,51 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Config;
use PhpMyAdmin\Git;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function strtotime;
final class GitInfoController extends AbstractController
{
/** @var Config */
private $config;
public function __construct(ResponseRenderer $response, Template $template, Config $config)
{
parent::__construct($response, $template);
$this->config = $config;
}
public function __invoke(): void
{
if (! $this->response->isAjax()) {
return;
}
$git = new Git($this->config->get('ShowGitRevision') ?? true);
if (! $git->isGitRevision()) {
return;
}
$commit = $git->checkGitRevision();
if (! $git->hasGitInformation() || $commit === null) {
$this->response->setRequestStatus(false);
return;
}
$commit['author']['date'] = Util::localisedDate(strtotime($commit['author']['date']));
$commit['committer']['date'] = Util::localisedDate(strtotime($commit['committer']['date']));
$this->render('home/git_info', $commit);
}
}

View file

@ -1,463 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Charsets;
use PhpMyAdmin\CheckUserPrivileges;
use PhpMyAdmin\Config;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Git;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\LanguageManager;
use PhpMyAdmin\Message;
use PhpMyAdmin\RecentFavoriteTable;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Server\Select;
use PhpMyAdmin\Template;
use PhpMyAdmin\ThemeManager;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use PhpMyAdmin\Version;
use function __;
use function count;
use function extension_loaded;
use function file_exists;
use function ini_get;
use function mb_strlen;
use function preg_match;
use function sprintf;
use const PHP_VERSION;
use const SODIUM_CRYPTO_SECRETBOX_KEYBYTES;
class HomeController extends AbstractController
{
/** @var Config */
private $config;
/** @var ThemeManager */
private $themeManager;
/** @var DatabaseInterface */
private $dbi;
/**
* @var array<int, array<string, string>>
* @psalm-var list<array{message: string, severity: 'warning'|'notice'}>
*/
private $errors = [];
public function __construct(
ResponseRenderer $response,
Template $template,
Config $config,
ThemeManager $themeManager,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->config = $config;
$this->themeManager = $themeManager;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $server, $collation_connection, $message, $show_query, $db, $table, $errorUrl;
if ($this->response->isAjax() && ! empty($_REQUEST['access_time'])) {
return;
}
$this->addScriptFiles(['home.js']);
// This is for $cfg['ShowDatabasesNavigationAsTree'] = false;
// See: https://github.com/phpmyadmin/phpmyadmin/issues/16520
// The DB is defined here and sent to the JS front-end to refresh the DB tree
$db = $_POST['db'] ?? '';
$table = '';
$show_query = '1';
$errorUrl = Url::getFromRoute('/');
if ($server > 0 && $this->dbi->isSuperUser()) {
$this->dbi->selectDb('mysql');
}
$languageManager = LanguageManager::getInstance();
if (! empty($message)) {
$displayMessage = Generator::getMessage($message);
unset($message);
}
if (isset($_SESSION['partial_logout'])) {
$partialLogout = Message::success(__(
'You were logged out from one server, to logout completely '
. 'from phpMyAdmin, you need to logout from all servers.'
))->getDisplay();
unset($_SESSION['partial_logout']);
}
$syncFavoriteTables = RecentFavoriteTable::getInstance('favorite')
->getHtmlSyncFavoriteTables();
$hasServer = $server > 0 || count($cfg['Servers']) > 1;
if ($hasServer) {
$hasServerSelection = $cfg['ServerDefault'] == 0
|| (! $cfg['NavigationDisplayServers']
&& (count($cfg['Servers']) > 1
|| ($server == 0 && count($cfg['Servers']) === 1)));
if ($hasServerSelection) {
$serverSelection = Select::render(true, true);
}
if ($server > 0) {
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
$checkUserPrivileges->getPrivileges();
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
$collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
$charsetsList = [];
foreach ($charsets as $charset) {
$collationsList = [];
foreach ($collations[$charset->getName()] as $collation) {
$collationsList[] = [
'name' => $collation->getName(),
'description' => $collation->getDescription(),
'is_selected' => $collation_connection === $collation->getName(),
];
}
$charsetsList[] = [
'name' => $charset->getName(),
'description' => $charset->getDescription(),
'collations' => $collationsList,
];
}
}
}
$availableLanguages = [];
if (empty($cfg['Lang']) && $languageManager->hasChoice()) {
$availableLanguages = $languageManager->sortedLanguages();
}
$databaseServer = [];
if ($server > 0 && $cfg['ShowServerInfo']) {
$hostInfo = '';
if (! empty($cfg['Server']['verbose'])) {
$hostInfo .= $cfg['Server']['verbose'];
$hostInfo .= ' (';
}
$hostInfo .= $this->dbi->getHostInfo();
if (! empty($cfg['Server']['verbose'])) {
$hostInfo .= ')';
}
$serverCharset = Charsets::getServerCharset($this->dbi, $cfg['Server']['DisableIS']);
$databaseServer = [
'host' => $hostInfo,
'type' => Util::getServerType(),
'connection' => Generator::getServerSSL(),
'version' => $this->dbi->getVersionString() . ' - ' . $this->dbi->getVersionComment(),
'protocol' => $this->dbi->getProtoInfo(),
'user' => $this->dbi->fetchValue('SELECT USER();'),
'charset' => $serverCharset->getDescription() . ' (' . $serverCharset->getName() . ')',
];
}
$webServer = [];
if ($cfg['ShowServerInfo']) {
$webServer['software'] = $_SERVER['SERVER_SOFTWARE'] ?? null;
if ($server > 0) {
$clientVersion = $this->dbi->getClientInfo();
if (preg_match('#\d+\.\d+\.\d+#', $clientVersion)) {
$clientVersion = 'libmysql - ' . $clientVersion;
}
$webServer['database'] = $clientVersion;
$webServer['php_extensions'] = Util::listPHPExtensions();
$webServer['php_version'] = PHP_VERSION;
}
}
$relation = new Relation($this->dbi);
if ($server > 0) {
$relationParameters = $relation->getRelationParameters();
if (! $relationParameters->hasAllFeatures() && $cfg['PmaNoRelation_DisableWarning'] == false) {
$messageText = __(
'The phpMyAdmin configuration storage is not completely '
. 'configured, some extended features have been deactivated. '
. '%sFind out why%s. '
);
if ($cfg['ZeroConf'] == true) {
$messageText .= '<br>' .
__('Or alternately go to \'Operations\' tab of any database to set it up there.');
}
$messageInstance = Message::notice($messageText);
$messageInstance->addParamHtml(
'<a href="' . Url::getFromRoute('/check-relations')
. '" data-post="' . Url::getCommon() . '">'
);
$messageInstance->addParamHtml('</a>');
/* Show error if user has configured something, notice elsewhere */
if (! empty($cfg['Servers'][$server]['pmadb'])) {
$messageInstance->isError(true);
}
$configStorageMessage = $messageInstance->getDisplay();
}
}
$this->checkRequirements();
$git = new Git($this->config->get('ShowGitRevision') ?? true);
$this->render('home/index', [
'db' => $db,
'table' => $table,
'message' => $displayMessage ?? '',
'partial_logout' => $partialLogout ?? '',
'is_git_revision' => $git->isGitRevision(),
'server' => $server,
'sync_favorite_tables' => $syncFavoriteTables,
'has_server' => $hasServer,
'is_demo' => $cfg['DBG']['demo'],
'has_server_selection' => $hasServerSelection ?? false,
'server_selection' => $serverSelection ?? '',
'has_change_password_link' => ($cfg['Server']['auth_type'] ?? '') !== 'config' && $cfg['ShowChgPassword'],
'charsets' => $charsetsList ?? [],
'available_languages' => $availableLanguages,
'database_server' => $databaseServer,
'web_server' => $webServer,
'show_php_info' => $cfg['ShowPhpInfo'],
'is_version_checked' => $cfg['VersionCheck'],
'phpmyadmin_version' => Version::VERSION,
'phpmyadmin_major_version' => Version::SERIES,
'config_storage_message' => $configStorageMessage ?? '',
'has_theme_manager' => $cfg['ThemeManager'],
'themes' => $this->themeManager->getThemesArray(),
'errors' => $this->errors,
]);
}
private function checkRequirements(): void
{
global $cfg, $server;
$this->checkPhpExtensionsRequirements();
if ($cfg['LoginCookieValidityDisableWarning'] == false) {
/**
* Check whether session.gc_maxlifetime limits session validity.
*/
$gc_time = (int) ini_get('session.gc_maxlifetime');
if ($gc_time < $cfg['LoginCookieValidity']) {
$this->errors[] = [
'message' => __(
'Your PHP parameter [a@https://www.php.net/manual/en/session.' .
'configuration.php#ini.session.gc-maxlifetime@_blank]session.' .
'gc_maxlifetime[/a] is lower than cookie validity configured ' .
'in phpMyAdmin, because of this, your login might expire sooner ' .
'than configured in phpMyAdmin.'
),
'severity' => 'warning',
];
}
}
/**
* Check whether LoginCookieValidity is limited by LoginCookieStore.
*/
if ($cfg['LoginCookieStore'] != 0 && $cfg['LoginCookieStore'] < $cfg['LoginCookieValidity']) {
$this->errors[] = [
'message' => __(
'Login cookie store is lower than cookie validity configured in ' .
'phpMyAdmin, because of this, your login will expire sooner than ' .
'configured in phpMyAdmin.'
),
'severity' => 'warning',
];
}
/**
* Warning if using the default MySQL controluser account
*/
if (
isset($cfg['Server']['controluser'], $cfg['Server']['controlpass'])
&& $server != 0
&& $cfg['Server']['controluser'] === 'pma'
&& $cfg['Server']['controlpass'] === 'pmapass'
) {
$this->errors[] = [
'message' => __(
'Your server is running with default values for the ' .
'controluser and password (controlpass) and is open to ' .
'intrusion; you really should fix this security weakness' .
' by changing the password for controluser \'pma\'.'
),
'severity' => 'warning',
];
}
/**
* Check if user does not have defined blowfish secret and it is being used.
*/
if (! empty($_SESSION['encryption_key'])) {
$encryptionKeyLength = mb_strlen($cfg['blowfish_secret'], '8bit');
if ($encryptionKeyLength < SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
$this->errors[] = [
'message' => __(
'The configuration file needs a valid key for cookie encryption.'
. ' A temporary key was automatically generated for you.'
. ' Please refer to the [doc@cfg_blowfish_secret]documentation[/doc].'
),
'severity' => 'warning',
];
} elseif ($encryptionKeyLength > SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
$this->errors[] = [
'message' => sprintf(
__(
'The cookie encryption key in the configuration file is longer than necessary.'
. ' It should only be %d bytes long.'
. ' Please refer to the [doc@cfg_blowfish_secret]documentation[/doc].'
),
SODIUM_CRYPTO_SECRETBOX_KEYBYTES
),
'severity' => 'warning',
];
}
}
/**
* Check for existence of config directory which should not exist in
* production environment.
*/
if (@file_exists(ROOT_PATH . 'config')) {
$this->errors[] = [
'message' => __(
'Directory [code]config[/code], which is used by the setup script, ' .
'still exists in your phpMyAdmin directory. It is strongly ' .
'recommended to remove it once phpMyAdmin has been configured. ' .
'Otherwise the security of your server may be compromised by ' .
'unauthorized people downloading your configuration.'
),
'severity' => 'warning',
];
}
/**
* Warning about Suhosin only if its simulation mode is not enabled
*/
if (
$cfg['SuhosinDisableWarning'] == false
&& ini_get('suhosin.request.max_value_length')
&& ini_get('suhosin.simulation') == '0'
) {
$this->errors[] = [
'message' => sprintf(
__(
'Server running with Suhosin. Please refer to %sdocumentation%s for possible issues.'
),
'[doc@faq1-38]',
'[/doc]'
),
'severity' => 'warning',
];
}
/* Missing template cache */
if ($this->config->getTempDir('twig') === null) {
$this->errors[] = [
'message' => sprintf(
__(
'The $cfg[\'TempDir\'] (%s) is not accessible. ' .
'phpMyAdmin is not able to cache templates and will ' .
'be slow because of this.'
),
$this->config->get('TempDir')
),
'severity' => 'warning',
];
}
$this->checkLanguageStats();
}
private function checkLanguageStats(): void
{
global $cfg, $lang;
/**
* Warning about incomplete translations.
*
* The data file is created while creating release by ./scripts/remove-incomplete-mo
*/
if (! @file_exists(ROOT_PATH . 'libraries/language_stats.inc.php')) {
return;
}
/** @psalm-suppress MissingFile */
include ROOT_PATH . 'libraries/language_stats.inc.php';
/*
* This message is intentionally not translated, because we're
* handling incomplete translations here and focus on english
* speaking users.
*/
if (
! isset($GLOBALS['language_stats'][$lang])
|| $GLOBALS['language_stats'][$lang] >= $cfg['TranslationWarningThreshold']
) {
return;
}
$this->errors[] = [
'message' => 'You are using an incomplete translation, please help to make it '
. 'better by [a@https://www.phpmyadmin.net/translate/'
. '@_blank]contributing[/a].',
'severity' => 'notice',
];
}
private function checkPhpExtensionsRequirements(): void
{
/**
* mbstring is used for handling multibytes inside parser, so it is good
* to tell user something might be broken without it, see bug #1063149.
*/
if (! extension_loaded('mbstring')) {
$this->errors[] = [
'message' => __(
'The mbstring PHP extension was not found and you seem to be using'
. ' a multibyte charset. Without the mbstring extension phpMyAdmin'
. ' is unable to split strings correctly and it may result in'
. ' unexpected results.'
),
'severity' => 'warning',
];
}
/**
* Missing functionality
*/
if (extension_loaded('curl') || ini_get('allow_url_fopen')) {
return;
}
$this->errors[] = [
'message' => __(
'The curl extension was not found and allow_url_fopen is '
. 'disabled. Due to this some features such as error reporting '
. 'or version check are disabled.'
),
'severity' => 'notice',
];
}
}

View file

@ -1,826 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Import;
use PhpMyAdmin\Bookmark;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Console;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Encoding;
use PhpMyAdmin\File;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Import;
use PhpMyAdmin\Message;
use PhpMyAdmin\ParseAnalyze;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Plugins\ImportPlugin;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Sql;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use PhpMyAdmin\Utils\ForeignKey;
use Throwable;
use function __;
use function _ngettext;
use function in_array;
use function ini_get;
use function ini_set;
use function intval;
use function is_array;
use function is_link;
use function is_numeric;
use function is_string;
use function is_uploaded_file;
use function mb_strlen;
use function mb_strtolower;
use function preg_match;
use function preg_quote;
use function preg_replace;
use function strlen;
use function substr;
use function time;
use function trim;
final class ImportController extends AbstractController
{
/** @var Import */
private $import;
/** @var Sql */
private $sql;
/** @var DatabaseInterface */
private $dbi;
public function __construct(
ResponseRenderer $response,
Template $template,
Import $import,
Sql $sql,
DatabaseInterface $dbi
) {
parent::__construct($response, $template);
$this->import = $import;
$this->sql = $sql;
$this->dbi = $dbi;
}
public function __invoke(): void
{
global $cfg, $collation_connection, $db, $import_type, $table, $goto, $display_query;
global $format, $local_import_file, $ajax_reload, $import_text, $sql_query, $message, $errorUrl, $urlParams;
global $memory_limit, $read_limit, $finished, $offset, $charset_conversion, $charset_of_file;
global $timestamp, $maximum_time, $timeout_passed, $import_file, $go_sql, $sql_file, $error, $max_sql_len, $msg;
global $sql_query_disabled, $executed_queries, $run_query, $reset_charset;
global $result, $import_file_name, $sql_data, $import_notice, $read_multiply, $my_die, $active_page;
global $show_as_php, $reload, $charset_connection, $is_js_confirmed, $MAX_FILE_SIZE, $message_to_show;
global $noplugin, $skip_queries;
$charset_of_file = $_POST['charset_of_file'] ?? null;
$format = $_POST['format'] ?? '';
$import_type = $_POST['import_type'] ?? null;
$is_js_confirmed = $_POST['is_js_confirmed'] ?? null;
$MAX_FILE_SIZE = $_POST['MAX_FILE_SIZE'] ?? null;
$message_to_show = $_POST['message_to_show'] ?? null;
$noplugin = $_POST['noplugin'] ?? null;
$skip_queries = $_POST['skip_queries'] ?? null;
$local_import_file = $_POST['local_import_file'] ?? null;
$show_as_php = $_POST['show_as_php'] ?? null;
// If it's a refresh console bookmarks request
if (isset($_GET['console_bookmark_refresh'])) {
$this->response->addJSON(
'console_message_bookmark',
Console::getBookmarkContent()
);
return;
}
// If it's a console bookmark add request
if (isset($_POST['console_bookmark_add'])) {
if (! isset($_POST['label'], $_POST['db'], $_POST['bookmark_query'], $_POST['shared'])) {
$this->response->addJSON('message', __('Incomplete params'));
return;
}
$bookmarkFields = [
'bkm_database' => $_POST['db'],
'bkm_user' => $cfg['Server']['user'],
'bkm_sql_query' => $_POST['bookmark_query'],
'bkm_label' => $_POST['label'],
];
$isShared = ($_POST['shared'] === 'true');
$bookmark = Bookmark::createBookmark($this->dbi, $bookmarkFields, $isShared);
if ($bookmark !== false && $bookmark->save()) {
$this->response->addJSON('message', __('Succeeded'));
$this->response->addJSON('data', $bookmarkFields);
$this->response->addJSON('isShared', $isShared);
} else {
$this->response->addJSON('message', __('Failed'));
}
return;
}
// reset import messages for ajax request
$_SESSION['Import_message']['message'] = null;
$_SESSION['Import_message']['go_back_url'] = null;
// default values
$reload = false;
// Use to identify current cycle is executing
// a multiquery statement or stored routine
if (! isset($_SESSION['is_multi_query'])) {
$_SESSION['is_multi_query'] = false;
}
$ajax_reload = [];
$import_text = '';
// Are we just executing plain query or sql file?
// (eg. non import, but query box/window run)
if (! empty($sql_query)) {
// apply values for parameters
if (! empty($_POST['parameterized']) && ! empty($_POST['parameters']) && is_array($_POST['parameters'])) {
$parameters = $_POST['parameters'];
foreach ($parameters as $parameter => $replacementValue) {
if (! is_numeric($replacementValue)) {
$replacementValue = '\'' . $this->dbi->escapeString($replacementValue) . '\'';
}
$quoted = preg_quote($parameter, '/');
// making sure that :param does not apply values to :param1
$sql_query = preg_replace(
'/' . $quoted . '([^a-zA-Z0-9_])/',
$replacementValue . '${1}',
$sql_query
);
// for parameters the appear at the end of the string
$sql_query = preg_replace('/' . $quoted . '$/', $replacementValue, $sql_query);
}
}
// run SQL query
$import_text = $sql_query;
$import_type = 'query';
$format = 'sql';
$_SESSION['sql_from_query_box'] = true;
// If there is a request to ROLLBACK when finished.
if (isset($_POST['rollback_query'])) {
$this->import->handleRollbackRequest($import_text);
}
// refresh navigation and main panels
if (preg_match('/^(DROP)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', $sql_query)) {
$reload = true;
$ajax_reload['reload'] = true;
}
// refresh navigation panel only
if (preg_match('/^(CREATE|ALTER)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', $sql_query)) {
$ajax_reload['reload'] = true;
}
// do a dynamic reload if table is RENAMED
// (by sending the instruction to the AJAX response handler)
if (preg_match('/^RENAME\s+TABLE\s+(.*?)\s+TO\s+(.*?)($|;|\s)/i', $sql_query, $rename_table_names)) {
$ajax_reload['reload'] = true;
$ajax_reload['table_name'] = Util::unQuote($rename_table_names[2]);
}
$sql_query = '';
} elseif (! empty($sql_file)) {
// run uploaded SQL file
$import_file = $sql_file;
$import_type = 'queryfile';
$format = 'sql';
unset($sql_file);
} elseif (! empty($_POST['id_bookmark'])) {
// run bookmark
$import_type = 'query';
$format = 'sql';
}
// If we didn't get any parameters, either user called this directly, or
// upload limit has been reached, let's assume the second possibility.
if ($_POST == [] && $_GET == []) {
$message = Message::error(
__(
'You probably tried to upload a file that is too large. Please refer ' .
'to %sdocumentation%s for a workaround for this limit.'
)
);
$message->addParam('[doc@faq1-16]');
$message->addParam('[/doc]');
// so we can obtain the message
$_SESSION['Import_message']['message'] = $message->getDisplay();
$_SESSION['Import_message']['go_back_url'] = $goto;
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $message);
return; // the footer is displayed automatically
}
// Add console message id to response output
if (isset($_POST['console_message_id'])) {
$this->response->addJSON('console_message_id', $_POST['console_message_id']);
}
/**
* Sets globals from $_POST patterns, for import plugins
* We only need to load the selected plugin
*/
if (! in_array($format, ['csv', 'ldi', 'mediawiki', 'ods', 'shp', 'sql', 'xml'])) {
// this should not happen for a normal user
// but only during an attack
Core::fatalError('Incorrect format parameter');
}
$post_patterns = [
'/^force_file_/',
'/^' . $format . '_/',
];
Core::setPostAsGlobal($post_patterns);
// Check needed parameters
Util::checkParameters(['import_type', 'format']);
// We don't want anything special in format
$format = Core::securePath($format);
if (strlen($table) > 0 && strlen($db) > 0) {
$urlParams = [
'db' => $db,
'table' => $table,
];
} elseif (strlen($db) > 0) {
$urlParams = ['db' => $db];
} else {
$urlParams = [];
}
// Create error and goto url
if ($import_type === 'table') {
$goto = Url::getFromRoute('/table/import');
} elseif ($import_type === 'database') {
$goto = Url::getFromRoute('/database/import');
} elseif ($import_type === 'server') {
$goto = Url::getFromRoute('/server/import');
} elseif (empty($goto) || ! preg_match('@^index\.php$@i', $goto)) {
if (strlen($table) > 0 && strlen($db) > 0) {
$goto = Url::getFromRoute('/table/structure');
} elseif (strlen($db) > 0) {
$goto = Url::getFromRoute('/database/structure');
} else {
$goto = Url::getFromRoute('/server/sql');
}
}
$errorUrl = $goto . Url::getCommon($urlParams, '&');
$_SESSION['Import_message']['go_back_url'] = $errorUrl;
if (strlen($db) > 0) {
$this->dbi->selectDb($db);
}
Util::setTimeLimit();
if (! empty($cfg['MemoryLimit'])) {
ini_set('memory_limit', $cfg['MemoryLimit']);
}
$timestamp = time();
$maximum_time = 0;
$maxExecutionTime = (int) ini_get('max_execution_time');
if (isset($_POST['allow_interrupt']) && $maxExecutionTime >= 1) {
$maximum_time = $maxExecutionTime - 1; // Give 1 second for phpMyAdmin to exit nicely
}
// set default values
$timeout_passed = false;
$error = false;
$read_multiply = 1;
$finished = false;
$offset = 0;
$max_sql_len = 0;
$sql_query = '';
$sql_query_disabled = false;
$go_sql = false;
$executed_queries = 0;
$run_query = true;
$charset_conversion = false;
$reset_charset = false;
$msg = 'Sorry an unexpected error happened!';
/** @var bool|mixed $result */
$result = false;
// Bookmark Support: get a query back from bookmark if required
if (! empty($_POST['id_bookmark'])) {
$id_bookmark = (int) $_POST['id_bookmark'];
switch ($_POST['action_bookmark']) {
case 0: // bookmarked query that have to be run
$bookmark = Bookmark::get(
$this->dbi,
$cfg['Server']['user'],
$db,
$id_bookmark,
'id',
isset($_POST['action_bookmark_all'])
);
if (! $bookmark instanceof Bookmark) {
break;
}
if (! empty($_POST['bookmark_variable'])) {
$import_text = $bookmark->applyVariables($_POST['bookmark_variable']);
} else {
$import_text = $bookmark->getQuery();
}
// refresh navigation and main panels
if (preg_match('/^(DROP)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', $import_text)) {
$reload = true;
$ajax_reload['reload'] = true;
}
// refresh navigation panel only
if (preg_match('/^(CREATE|ALTER)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', $import_text)) {
$ajax_reload['reload'] = true;
}
break;
case 1: // bookmarked query that have to be displayed
$bookmark = Bookmark::get($this->dbi, $cfg['Server']['user'], $db, $id_bookmark);
if (! $bookmark instanceof Bookmark) {
break;
}
$import_text = $bookmark->getQuery();
if ($this->response->isAjax()) {
$message = Message::success(__('Showing bookmark'));
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
$this->response->addJSON('sql_query', $import_text);
$this->response->addJSON('action_bookmark', $_POST['action_bookmark']);
return;
} else {
$run_query = false;
}
break;
case 2: // bookmarked query that have to be deleted
$bookmark = Bookmark::get($this->dbi, $cfg['Server']['user'], $db, $id_bookmark);
if (! $bookmark instanceof Bookmark) {
break;
}
$bookmark->delete();
if ($this->response->isAjax()) {
$message = Message::success(
__('The bookmark has been deleted.')
);
$this->response->setRequestStatus($message->isSuccess());
$this->response->addJSON('message', $message);
$this->response->addJSON('action_bookmark', $_POST['action_bookmark']);
$this->response->addJSON('id_bookmark', $id_bookmark);
return;
} else {
$run_query = false;
$error = true; // this is kind of hack to skip processing the query
}
break;
}
}
// Do no run query if we show PHP code
if (isset($show_as_php)) {
$run_query = false;
$go_sql = true;
}
// We can not read all at once, otherwise we can run out of memory
$memory_limit = trim((string) ini_get('memory_limit'));
// 2 MB as default
if (empty($memory_limit)) {
$memory_limit = 2 * 1024 * 1024;
}
// In case no memory limit we work on 10MB chunks
if ($memory_limit === '-1') {
$memory_limit = 10 * 1024 * 1024;
}
// Calculate value of the limit
$memoryUnit = mb_strtolower(substr((string) $memory_limit, -1));
if ($memoryUnit === 'm') {
$memory_limit = (int) substr((string) $memory_limit, 0, -1) * 1024 * 1024;
} elseif ($memoryUnit === 'k') {
$memory_limit = (int) substr((string) $memory_limit, 0, -1) * 1024;
} elseif ($memoryUnit === 'g') {
$memory_limit = (int) substr((string) $memory_limit, 0, -1) * 1024 * 1024 * 1024;
} else {
$memory_limit = (int) $memory_limit;
}
// Just to be sure, there might be lot of memory needed for uncompression
$read_limit = $memory_limit / 8;
// handle filenames
if (
isset($_FILES['import_file'])
&& is_array($_FILES['import_file'])
&& isset($_FILES['import_file']['name'], $_FILES['import_file']['tmp_name'])
&& is_string($_FILES['import_file']['name'])
&& is_string($_FILES['import_file']['tmp_name'])
) {
$import_file = $_FILES['import_file']['tmp_name'];
$import_file_name = $_FILES['import_file']['name'];
}
if (! empty($local_import_file) && ! empty($cfg['UploadDir'])) {
// sanitize $local_import_file as it comes from a POST
$local_import_file = Core::securePath($local_import_file);
$import_file = Util::userDir((string) $cfg['UploadDir'])
. $local_import_file;
/*
* Do not allow symlinks to avoid security issues
* (user can create symlink to file they can not access,
* but phpMyAdmin can).
*/
if (@is_link($import_file)) {
$import_file = 'none';
}
} elseif (empty($import_file) || ! is_uploaded_file($import_file)) {
$import_file = 'none';
}
// Do we have file to import?
if ($import_file !== 'none' && ! $error) {
/**
* Handle file compression
*/
$importHandle = new File($import_file);
$importHandle->checkUploadedFile();
if ($importHandle->isError()) {
/** @var Message $errorMessage */
$errorMessage = $importHandle->getError();
$importHandle->close();
$_SESSION['Import_message']['message'] = $errorMessage->getDisplay();
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $errorMessage->getDisplay());
$this->response->addHTML($errorMessage->getDisplay());
return;
}
$importHandle->setDecompressContent(true);
$importHandle->open();
if ($importHandle->isError()) {
/** @var Message $errorMessage */
$errorMessage = $importHandle->getError();
$importHandle->close();
$_SESSION['Import_message']['message'] = $errorMessage->getDisplay();
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $errorMessage->getDisplay());
$this->response->addHTML($errorMessage->getDisplay());
return;
}
} elseif (! $error && (! isset($import_text) || empty($import_text))) {
$message = Message::error(
__(
'No data was received to import. Either no file name was ' .
'submitted, or the file size exceeded the maximum size permitted ' .
'by your PHP configuration. See [doc@faq1-16]FAQ 1.16[/doc].'
)
);
$_SESSION['Import_message']['message'] = $message->getDisplay();
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $message->getDisplay());
$this->response->addHTML($message->getDisplay());
return;
}
// Convert the file's charset if necessary
if (Encoding::isSupported() && isset($charset_of_file)) {
if ($charset_of_file !== 'utf-8') {
$charset_conversion = true;
}
} elseif (isset($charset_of_file) && $charset_of_file !== 'utf-8') {
$this->dbi->query('SET NAMES \'' . $charset_of_file . '\'');
// We can not show query in this case, it is in different charset
$sql_query_disabled = true;
$reset_charset = true;
}
// Something to skip? (because timeout has passed)
if (! $error && isset($_POST['skip'])) {
$original_skip = $skip = intval($_POST['skip']);
while ($skip > 0 && ! $finished) {
$this->import->getNextChunk($importHandle ?? null, $skip < $read_limit ? $skip : $read_limit);
// Disable read progressivity, otherwise we eat all memory!
$read_multiply = 1;
$skip -= $read_limit;
}
unset($skip);
}
// This array contain the data like number of valid sql queries in the statement
// and complete valid sql statement (which affected for rows)
$sql_data = [
'valid_sql' => [],
'valid_queries' => 0,
];
if (! $error) {
/**
* @var ImportPlugin $import_plugin
*/
$import_plugin = Plugins::getPlugin('import', $format, $import_type);
if ($import_plugin == null) {
$message = Message::error(
__('Could not load import plugins, please check your installation!')
);
$_SESSION['Import_message']['message'] = $message->getDisplay();
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $message->getDisplay());
$this->response->addHTML($message->getDisplay());
return;
}
// Do the real import
$default_fk_check = ForeignKey::handleDisableCheckInit();
try {
$import_plugin->doImport($importHandle ?? null, $sql_data);
ForeignKey::handleDisableCheckCleanup($default_fk_check);
} catch (Throwable $e) {
ForeignKey::handleDisableCheckCleanup($default_fk_check);
throw $e;
}
}
if (isset($importHandle)) {
$importHandle->close();
}
// Reset charset back, if we did some changes
if ($reset_charset) {
$this->dbi->query('SET CHARACTER SET ' . $charset_connection);
$this->dbi->setCollation($collation_connection);
}
// Show correct message
if (! empty($id_bookmark) && $_POST['action_bookmark'] == 2) {
$message = Message::success(__('The bookmark has been deleted.'));
$display_query = $import_text;
$error = false; // unset error marker, it was used just to skip processing
} elseif (! empty($id_bookmark) && $_POST['action_bookmark'] == 1) {
$message = Message::notice(__('Showing bookmark'));
} elseif ($finished && ! $error) {
// Do not display the query with message, we do it separately
$display_query = ';';
if ($import_type !== 'query') {
$message = Message::success(
'<em>'
. _ngettext(
'Import has been successfully finished, %d query executed.',
'Import has been successfully finished, %d queries executed.',
$executed_queries
)
. '</em>'
);
$message->addParam($executed_queries);
if (! empty($import_notice)) {
$message->addHtml($import_notice);
}
if (! empty($local_import_file)) {
$message->addText('(' . $local_import_file . ')');
} elseif (
isset($_FILES['import_file'])
&& is_array($_FILES['import_file'])
&& isset($_FILES['import_file']['name'])
&& is_string($_FILES['import_file']['name'])
) {
$message->addText('(' . $_FILES['import_file']['name'] . ')');
}
}
}
// Did we hit timeout? Tell it user.
if ($timeout_passed) {
$urlParams['timeout_passed'] = '1';
$urlParams['offset'] = $offset;
if (isset($local_import_file)) {
$urlParams['local_import_file'] = $local_import_file;
}
$importUrl = $errorUrl = $goto . Url::getCommon($urlParams, '&');
$message = Message::error(
__(
'Script timeout passed, if you want to finish import,'
. ' please %sresubmit the same file%s and import will resume.'
)
);
$message->addParamHtml('<a href="' . $importUrl . '">');
$message->addParamHtml('</a>');
if ($offset == 0 || (isset($original_skip) && $original_skip == $offset)) {
$message->addText(
__(
'However on last run no data has been parsed,'
. ' this usually means phpMyAdmin won\'t be able to'
. ' finish this import unless you increase php time limits.'
)
);
}
}
// if there is any message, copy it into $_SESSION as well,
// so we can obtain it by AJAX call
if (isset($message)) {
$_SESSION['Import_message']['message'] = $message->getDisplay();
}
// Parse and analyze the query, for correct db and table name
// in case of a query typed in the query window
// (but if the query is too large, in case of an imported file, the parser
// can choke on it so avoid parsing)
$sqlLength = mb_strlen($sql_query);
if ($sqlLength <= $cfg['MaxCharactersInDisplayedSQL']) {
[
$analyzed_sql_results,
$db,
$table_from_sql,
] = ParseAnalyze::sqlQuery($sql_query, $db);
$reload = $analyzed_sql_results['reload'];
$offset = $analyzed_sql_results['offset'];
if ($table != $table_from_sql && ! empty($table_from_sql)) {
$table = $table_from_sql;
}
}
// There was an error?
if (isset($my_die)) {
foreach ($my_die as $die) {
Generator::mysqlDie($die['error'], $die['sql'], false, $errorUrl, $error);
}
}
if ($go_sql) {
if (! empty($sql_data) && ($sql_data['valid_queries'] > 1)) {
$_SESSION['is_multi_query'] = true;
$sql_queries = $sql_data['valid_sql'];
} else {
$sql_queries = [$sql_query];
}
$html_output = '';
foreach ($sql_queries as $sql_query) {
// parse sql query
[
$analyzed_sql_results,
$db,
$table_from_sql,
] = ParseAnalyze::sqlQuery($sql_query, $db);
$offset = $analyzed_sql_results['offset'];
$reload = $analyzed_sql_results['reload'];
// Check if User is allowed to issue a 'DROP DATABASE' Statement
if (
$this->sql->hasNoRightsToDropDatabase(
$analyzed_sql_results,
$cfg['AllowUserDropDatabase'],
$this->dbi->isSuperUser()
)
) {
Generator::mysqlDie(
__('"DROP DATABASE" statements are disabled.'),
'',
false,
$_SESSION['Import_message']['go_back_url']
);
return;
}
if ($table != $table_from_sql && ! empty($table_from_sql)) {
$table = $table_from_sql;
}
$html_output .= $this->sql->executeQueryAndGetQueryResponse(
$analyzed_sql_results, // analyzed_sql_results
false, // is_gotofile
$db, // db
$table, // table
null, // find_real_end
null, // sql_query_for_bookmark - see below
null, // extra_data
null, // message_to_show
null, // sql_data
$goto, // goto
null, // disp_query
null, // disp_message
$sql_query, // sql_query
null // complete_query
);
}
// sql_query_for_bookmark is not included in Sql::executeQueryAndGetQueryResponse
// since only one bookmark has to be added for all the queries submitted through
// the SQL tab
if (! empty($_POST['bkm_label']) && ! empty($import_text)) {
$relation = new Relation($this->dbi);
$this->sql->storeTheQueryAsBookmark(
$relation->getRelationParameters()->bookmarkFeature,
$db,
$cfg['Server']['user'],
$_POST['sql_query'],
$_POST['bkm_label'],
isset($_POST['bkm_replace'])
);
}
$this->response->addJSON('ajax_reload', $ajax_reload);
$this->response->addHTML($html_output);
return;
}
if ($result) {
// Save a Bookmark with more than one queries (if Bookmark label given).
if (! empty($_POST['bkm_label']) && ! empty($import_text)) {
$relation = new Relation($this->dbi);
$this->sql->storeTheQueryAsBookmark(
$relation->getRelationParameters()->bookmarkFeature,
$db,
$cfg['Server']['user'],
$_POST['sql_query'],
$_POST['bkm_label'],
isset($_POST['bkm_replace'])
);
}
$this->response->setRequestStatus(true);
$this->response->addJSON('message', Message::success($msg));
$this->response->addJSON(
'sql_query',
Generator::getMessage($msg, $sql_query, 'success')
);
} elseif ($result === false) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', Message::error($msg));
} else {
$active_page = $goto;
/** @psalm-suppress UnresolvableInclude */
include ROOT_PATH . $goto;
}
// If there is request for ROLLBACK in the end.
if (! isset($_POST['rollback_query'])) {
return;
}
$this->dbi->query('ROLLBACK');
}
}

View file

@ -1,93 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Import;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Import\SimulateDml;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\SqlParser\Statements\DeleteStatement;
use PhpMyAdmin\SqlParser\Statements\UpdateStatement;
use PhpMyAdmin\SqlParser\Utils\Query;
use PhpMyAdmin\Template;
use function __;
use function count;
use function explode;
final class SimulateDmlController extends AbstractController
{
/** @var SimulateDml */
private $simulateDml;
public function __construct(
ResponseRenderer $response,
Template $template,
SimulateDml $simulateDml
) {
parent::__construct($response, $template);
$this->simulateDml = $simulateDml;
}
public function __invoke(): void
{
$error = '';
$errorMsg = __('Only single-table UPDATE and DELETE queries can be simulated.');
/** @var string $sqlDelimiter */
$sqlDelimiter = $_POST['sql_delimiter'];
$sqlData = [];
/** @var string[] $queries */
$queries = explode($sqlDelimiter, $GLOBALS['sql_query']);
foreach ($queries as $sqlQuery) {
if (empty($sqlQuery)) {
continue;
}
// Parsing the query.
$parser = new Parser($sqlQuery);
if (empty($parser->statements[0])) {
continue;
}
$statement = $parser->statements[0];
if (
! ($statement instanceof UpdateStatement || $statement instanceof DeleteStatement)
|| ! empty($statement->join)
) {
$error = $errorMsg;
break;
}
$tables = Query::getTables($statement);
if (count($tables) > 1) {
$error = $errorMsg;
break;
}
// Get the matched rows for the query.
$result = $this->simulateDml->getMatchedRows($sqlQuery, $parser, $statement);
$error = $this->simulateDml->getError();
if ($error !== '') {
break;
}
$sqlData[] = $result;
}
if ($error) {
$message = Message::rawError($error);
$this->response->addJSON('message', $message);
$this->response->addJSON('sql_data', false);
return;
}
$this->response->addJSON('sql_data', $sqlData);
}
}

View file

@ -1,83 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Import;
use PhpMyAdmin\Core;
use PhpMyAdmin\Import\Ajax;
use PhpMyAdmin\Message;
use PhpMyAdmin\Template;
use function __;
use function header;
use function ini_get;
use function session_start;
use function session_write_close;
use function time;
use function usleep;
/**
* Import progress bar backend
*/
class StatusController
{
/** @var Template */
private $template;
public function __construct(Template $template)
{
$this->template = $template;
}
public function __invoke(): void
{
global $SESSION_KEY, $upload_id, $plugins, $timestamp;
[
$SESSION_KEY,
$upload_id,
$plugins,
] = Ajax::uploadProgressSetup();
// $_GET["message"] is used for asking for an import message
if (isset($_GET['message']) && $_GET['message']) {
// AJAX requests can't be cached!
Core::noCacheHeader();
header('Content-type: text/html');
// wait 0.3 sec before we check for $_SESSION variable
usleep(300000);
$maximumTime = ini_get('max_execution_time');
$timestamp = time();
// wait until message is available
while (($_SESSION['Import_message']['message'] ?? null) == null) {
// close session before sleeping
session_write_close();
// sleep
usleep(250000); // 0.25 sec
// reopen session
session_start();
if (time() - $timestamp > $maximumTime) {
$_SESSION['Import_message']['message'] = Message::error(
__('Could not load the progress of the import.')
)->getDisplay();
break;
}
}
echo $_SESSION['Import_message']['message'] ?? '';
if (isset($_SESSION['Import_message']['go_back_url'])) {
echo $this->template->render('import_status', [
'go_back_url' => $_SESSION['Import_message']['go_back_url'],
]);
}
} else {
Ajax::status($_GET['id']);
}
}
}

View file

@ -1,713 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Theme;
use function __;
use function _pgettext;
use function json_encode;
/**
* Exporting of translated messages from PHP to JavaScript.
*/
final class JavaScriptMessagesController
{
/** @var array<string, string> */
private $messages = [];
public function __construct()
{
$this->setMessages();
}
public function __invoke(): void
{
echo 'var Messages = ' . json_encode($this->messages) . ';';
}
private function setMessages(): void
{
global $cfg, $theme;
$ajaxClockSmallGifPath = $theme instanceof Theme ? $theme->getImgPath('ajax_clock_small.gif') : '';
$this->messages = [
/* For confirmations */
'strConfirm' => __('Confirm'),
'strDoYouReally' => __('Do you really want to execute "%s"?'),
'strDropDatabaseStrongWarning' => __('You are about to DESTROY a complete database!'),
'strDatabaseRenameToSameName' => __(
'Cannot rename database to the same name. Change the name and try again'
),
'strDropTableStrongWarning' => __('You are about to DESTROY a complete table!'),
'strTruncateTableStrongWarning' => __('You are about to TRUNCATE a complete table!'),
'strDeleteTableStrongWarning' => __('You are about to DELETE all the rows of the table!'),
'strDeleteTrackingData' => __('Delete tracking data for this table?'),
'strDeleteTrackingDataMultiple' => __('Delete tracking data for these tables?'),
'strDeleteTrackingVersion' => __('Delete tracking data for this version?'),
'strDeleteTrackingVersionMultiple' => __('Delete tracking data for these versions?'),
'strDeletingTrackingEntry' => __('Delete entry from tracking report?'),
'strDeletingTrackingData' => __('Deleting tracking data'),
'strDroppingPrimaryKeyIndex' => __('Dropping Primary Key/Index'),
'strDroppingForeignKey' => __('Dropping Foreign key.'),
'strOperationTakesLongTime' => __('This operation could take a long time. Proceed anyway?'),
'strDropUserGroupWarning' => __('Do you really want to delete user group "%s"?'),
'strConfirmDeleteQBESearch' => __('Do you really want to delete the search "%s"?'),
'strConfirmNavigation' => __('You have unsaved changes; are you sure you want to leave this page?'),
'strConfirmRowChange' => __(
'You are trying to reduce the number of rows, but have already entered'
. ' data in those rows which will be lost. Do you wish to continue?'
),
'strDropUserWarning' => __('Do you really want to revoke the selected user(s) ?'),
'strDeleteCentralColumnWarning' => __('Do you really want to delete this central column?'),
'strDropRTEitems' => __('Do you really want to delete the selected items?'),
'strDropPartitionWarning' => __(
'Do you really want to DROP the selected partition(s)? This will also DELETE ' .
'the data related to the selected partition(s)!'
),
'strTruncatePartitionWarning' => __('Do you really want to TRUNCATE the selected partition(s)?'),
'strRemovePartitioningWarning' => __('Do you really want to remove partitioning?'),
'strResetReplicaWarning' => __('Do you really want to reset the replica (RESET REPLICA)?'),
'strChangeColumnCollation' => __(
'This operation will attempt to convert your data to the new collation. In '
. 'rare cases, especially where a character doesn\'t exist in the new '
. 'collation, this process could cause the data to appear incorrectly under '
. 'the new collation; in this case we suggest you revert to the original '
. 'collation and refer to the tips at '
)
. '<a href="%s" target="garbled_data_wiki">' . __('Garbled Data') . '</a>.'
. '<br><br>'
. __('Are you sure you wish to change the collation and convert the data?'),
'strChangeAllColumnCollationsWarning' => __(
'Through this operation, MySQL attempts to map the data values between '
. 'collations. If the character sets are incompatible, there may be data loss '
. 'and this lost data may <b>NOT</b> be recoverable simply by changing back the '
. 'column collation(s). <b>To convert existing data, it is suggested to use the '
. 'column(s) editing feature (the "Change" Link) on the table structure page. '
. '</b>'
)
. '<br><br>'
. __('Are you sure you wish to change all the column collations and convert the data?'),
/* For modal dialog buttons */
'strSaveAndClose' => __('Save & close'),
'strReset' => __('Reset'),
'strResetAll' => __('Reset all'),
/* For indexes */
'strFormEmpty' => __('Missing value in the form!'),
'strRadioUnchecked' => __('Select at least one of the options!'),
'strEnterValidNumber' => __('Please enter a valid number!'),
'strEnterValidLength' => __('Please enter a valid length!'),
'strAddIndex' => __('Add index'),
'strEditIndex' => __('Edit index'),
/* l10n: Rename a table Index */
'strRenameIndex' => __('Rename index'),
'strAddToIndex' => __('Add %s column(s) to index'),
'strCreateSingleColumnIndex' => __('Create single-column index'),
'strCreateCompositeIndex' => __('Create composite index'),
'strCompositeWith' => __('Composite with:'),
'strMissingColumn' => __('Please select column(s) for the index.'),
/* For Preview SQL*/
'strPreviewSQL' => __('Preview SQL'),
/* For Simulate DML*/
'strSimulateDML' => __('Simulate query'),
'strMatchedRows' => __('Matched rows:'),
'strSQLQuery' => __('SQL query:'),
/* Charts */
/* l10n: Default label for the y-Axis of Charts */
'strYValues' => __('Y values'),
/* Database multi-table query */
'strEmptyQuery' => __('Please enter the SQL query first.'),
/* For server/privileges.js */
'strHostEmpty' => __('The host name is empty!'),
'strUserEmpty' => __('The user name is empty!'),
'strPasswordEmpty' => __('The password is empty!'),
'strPasswordNotSame' => __('The passwords aren\'t the same!'),
'strRemovingSelectedUsers' => __('Removing Selected Users'),
'strClose' => __('Close'),
'strLock' => _pgettext('Lock the account.', 'Lock'),
'strUnlock' => _pgettext('Unlock the account.', 'Unlock'),
'strLockAccount' => __('Lock this account.'),
'strUnlockAccount' => __('Unlock this account.'),
/* For export.js */
'strTemplateCreated' => __('Template was created.'),
'strTemplateLoaded' => __('Template was loaded.'),
'strTemplateUpdated' => __('Template was updated.'),
'strTemplateDeleted' => __('Template was deleted.'),
/* l10n: Other, small valued, queries */
'strOther' => __('Other'),
/* l10n: Thousands separator */
'strThousandsSeparator' => __(','),
/* l10n: Decimal separator */
'strDecimalSeparator' => __('.'),
'strChartConnectionsTitle' => __('Connections / Processes'),
/* server status monitor */
'strIncompatibleMonitorConfig' => __('Local monitor configuration incompatible!'),
'strIncompatibleMonitorConfigDescription' => __(
'The chart arrangement configuration in your browsers local storage is not '
. 'compatible anymore to the newer version of the monitor dialog. It is very '
. 'likely that your current configuration will not work anymore. Please reset '
. 'your configuration to default in the <i>Settings</i> menu.'
),
'strQueryCacheEfficiency' => __('Query cache efficiency'),
'strQueryCacheUsage' => __('Query cache usage'),
'strQueryCacheUsed' => __('Query cache used'),
'strSystemCPUUsage' => __('System CPU usage'),
'strSystemMemory' => __('System memory'),
'strSystemSwap' => __('System swap'),
'strAverageLoad' => __('Average load'),
'strTotalMemory' => __('Total memory'),
'strCachedMemory' => __('Cached memory'),
'strBufferedMemory' => __('Buffered memory'),
'strFreeMemory' => __('Free memory'),
'strUsedMemory' => __('Used memory'),
'strTotalSwap' => __('Total swap'),
'strCachedSwap' => __('Cached swap'),
'strUsedSwap' => __('Used swap'),
'strFreeSwap' => __('Free swap'),
'strBytesSent' => __('Bytes sent'),
'strBytesReceived' => __('Bytes received'),
'strConnections' => __('Connections'),
'strProcesses' => __('Processes'),
/* summary row */
'strB' => __('B'),
'strKiB' => __('KiB'),
'strMiB' => __('MiB'),
'strGiB' => __('GiB'),
'strTiB' => __('TiB'),
'strPiB' => __('PiB'),
'strEiB' => __('EiB'),
'strNTables' => __('%d table(s)'),
/* l10n: Questions is the name of a MySQL Status variable */
'strQuestions' => __('Questions'),
'strTraffic' => __('Traffic'),
'strSettings' => __('Settings'),
'strAddChart' => __('Add chart to grid'),
'strAddOneSeriesWarning' => __('Please add at least one variable to the series!'),
'strNone' => __('None'),
/* l10n: SQL Query on modal to show exported query */
'strQuery' => __('SQL Query'),
'strResumeMonitor' => __('Resume monitor'),
'strPauseMonitor' => __('Pause monitor'),
'strStartRefresh' => __('Start auto refresh'),
'strStopRefresh' => __('Stop auto refresh'),
/* Monitor: Instructions Dialog */
'strBothLogOn' => __('general_log and slow_query_log are enabled.'),
'strGenLogOn' => __('general_log is enabled.'),
'strSlowLogOn' => __('slow_query_log is enabled.'),
'strBothLogOff' => __('slow_query_log and general_log are disabled.'),
'strLogOutNotTable' => __('log_output is not set to TABLE.'),
'strLogOutIsTable' => __('log_output is set to TABLE.'),
'strSmallerLongQueryTimeAdvice' => __(
'slow_query_log is enabled, but the server logs only queries that take longer '
. 'than %d seconds. It is advisable to set this long_query_time 0-2 seconds, '
. 'depending on your system.'
),
'strLongQueryTimeSet' => __('long_query_time is set to %d second(s).'),
'strSettingsAppliedGlobal' => __(
'Following settings will be applied globally and reset to default on server restart:'
),
/* l10n: %s is FILE or TABLE */
'strSetLogOutput' => __('Set log_output to %s'),
/* l10n: Enable in this context means setting a status variable to ON */
'strEnableVar' => __('Enable %s'),
/* l10n: Disable in this context means setting a status variable to OFF */
'strDisableVar' => __('Disable %s'),
/* l10n: %d seconds */
'setSetLongQueryTime' => __('Set long_query_time to %d seconds.'),
'strNoSuperUser' => __(
'You can\'t change these variables. Please log in as root or contact your database administrator.'
),
'strChangeSettings' => __('Change settings'),
'strCurrentSettings' => __('Current settings'),
'strChartTitle' => __('Chart title'),
/* l10n: As in differential values */
'strDifferential' => __('Differential'),
'strDividedBy' => __('Divided by %s'),
'strUnit' => __('Unit'),
'strFromSlowLog' => __('From slow log'),
'strFromGeneralLog' => __('From general log'),
'strServerLogError' => __('The database name is not known for this query in the server\'s logs.'),
'strAnalysingLogsTitle' => __('Analysing logs'),
'strAnalysingLogs' => __('Analysing & loading logs. This may take a while.'),
'strCancelRequest' => __('Cancel request'),
'strCountColumnExplanation' => __(
'This column shows the amount of identical queries that are grouped together. '
. 'However only the SQL query itself has been used as a grouping criteria, so '
. 'the other attributes of queries, such as start time, may differ.'
),
'strMoreCountColumnExplanation' => __(
'Since grouping of INSERTs queries has been selected, INSERT queries into the '
. 'same table are also being grouped together, disregarding of the inserted '
. 'data.'
),
'strLogDataLoaded' => __('Log data loaded. Queries executed in this time span:'),
'strJumpToTable' => __('Jump to Log table'),
'strNoDataFoundTitle' => __('No data found'),
'strNoDataFound' => __('Log analysed, but no data found in this time span.'),
'strAnalyzing' => __('Analyzing…'),
'strExplainOutput' => __('Explain output'),
'strStatus' => __('Status'),
'strTime' => __('Time'),
'strTotalTime' => __('Total time:'),
'strProfilingResults' => __('Profiling results'),
'strTable' => _pgettext('Display format', 'Table'),
'strChart' => __('Chart'),
'strAliasDatabase' => _pgettext('Alias', 'Database'),
'strAliasTable' => _pgettext('Alias', 'Table'),
'strAliasColumn' => _pgettext('Alias', 'Column'),
/* l10n: A collection of available filters */
'strFiltersForLogTable' => __('Log table filter options'),
/* l10n: Filter as in "Start Filtering" */
'strFilter' => __('Filter'),
'strFilterByWordRegexp' => __('Filter queries by word/regexp:'),
'strIgnoreWhereAndGroup' => __('Group queries, ignoring variable data in WHERE clauses'),
'strSumRows' => __('Sum of grouped rows:'),
'strTotal' => __('Total:'),
'strLoadingLogs' => __('Loading logs'),
'strRefreshFailed' => __('Monitor refresh failed'),
'strInvalidResponseExplanation' => __(
'While requesting new chart data the server returned an invalid response. This '
. 'is most likely because your session expired. Reloading the page and '
. 'reentering your credentials should help.'
),
'strReloadPage' => __('Reload page'),
'strAffectedRows' => __('Affected rows:'),
'strFailedParsingConfig' => __('Failed parsing config file. It doesn\'t seem to be valid JSON code.'),
'strFailedBuildingGrid' => __(
'Failed building chart grid with imported config. Resetting to default config…'
),
'strImport' => __('Import'),
'strImportDialogTitle' => __('Import monitor configuration'),
'strImportDialogMessage' => __('Please select the file you want to import:'),
'strTableNameDialogMessage' => __('Please enter a valid table name.'),
'strDBNameDialogMessage' => __('Please enter a valid database name.'),
'strNoImportFile' => __('No files available on server for import!'),
'strAnalyzeQuery' => __('Analyse query'),
/* For query editor */
'strFormatting' => __('Formatting SQL…'),
'strNoParam' => __('No parameters found!'),
/* For inline query editing */
'strGo' => __('Go'),
'strCancel' => __('Cancel'),
/* For page-related settings */
'strPageSettings' => __('Page-related settings'),
'strApply' => __('Apply'),
/* For Ajax Notifications */
'strLoading' => __('Loading…'),
'strAbortedRequest' => __('Request aborted!!'),
'strProcessingRequest' => __('Processing request'),
'strRequestFailed' => __('Request failed!!'),
'strErrorProcessingRequest' => __('Error in processing request'),
'strErrorCode' => __('Error code: %s'),
'strErrorText' => __('Error text: %s'),
'strErrorConnection' => __(
'It seems that the connection to server has been lost. Please check your ' .
'network connectivity and server status.'
),
'strNoDatabasesSelected' => __('No databases selected.'),
'strNoTableSelected' => __('No table selected.'),
'strNoAccountSelected' => __('No accounts selected.'),
'strDroppingColumn' => __('Dropping column'),
'strAddingPrimaryKey' => __('Adding primary key'),
'strOK' => __('OK'),
'strDismiss' => __('Click to dismiss this notification'),
/* For database/operations.js */
'strRenamingDatabases' => __('Renaming databases'),
'strCopyingDatabase' => __('Copying database'),
'strChangingCharset' => __('Changing charset'),
'strNo' => __('No'),
/* For Foreign key checks */
'strForeignKeyCheck' => __('Enable foreign key checks'),
/* For database/structure.js */
'strErrorRealRowCount' => __('Failed to get real row count.'),
/* For database/search.js */
'strSearching' => __('Searching'),
'strHideSearchResults' => __('Hide search results'),
'strShowSearchResults' => __('Show search results'),
'strBrowsing' => __('Browsing'),
'strDeleting' => __('Deleting'),
'strConfirmDeleteResults' => __('Delete the matches for the %s table?'),
/* For rte.js */
'MissingReturn' => __('The definition of a stored function must contain a RETURN statement!'),
'strExport' => __('Export'),
'NoExportable' => __('No routine is exportable. Required privileges may be lacking.'),
/* For ENUM/SET editor*/
'enum_columnVals' => __('Values for column %s'),
'enum_newColumnVals' => __('Values for a new column'),
'enum_hint' => __('Enter each value in a separate field.'),
'enum_addValue' => __('Add %d value(s)'),
/* For import.js */
'strImportCSV' => __('Note: If the file contains multiple tables, they will be combined into one.'),
/* For sql.js */
'strHideQueryBox' => __('Hide query box'),
'strShowQueryBox' => __('Show query box'),
'strEdit' => __('Edit'),
'strDelete' => __('Delete'),
'strNotValidRowNumber' => __('%d is not valid row number.'),
'strBrowseForeignValues' => __('Browse foreign values'),
'strNoAutoSavedQuery' => __('No previously auto-saved query is available. Loading default query.'),
'strPreviousSaveQuery' => __(
'You have a previously saved query. Click Get auto-saved query to load the query.'
),
'strBookmarkVariable' => __('Variable %d:'),
/* For Central list of columns */
'pickColumn' => __('Pick'),
'pickColumnTitle' => __('Column selector'),
'searchList' => __('Search this list'),
'strEmptyCentralList' => __(
'No columns in the central list. Make sure the Central columns list for '
. 'database %s has columns that are not present in the current table.'
),
'seeMore' => __('See more'),
/* For normalization */
'strAddPrimaryKey' => __('Add primary key'),
'strPrimaryKeyAdded' => __('Primary key added.'),
'strToNextStep' => __('Taking you to next step…'),
'strFinishMsg' => __("The first step of normalization is complete for table '%s'."),
'strEndStep' => __('End of step'),
'str2NFNormalization' => __('Second step of normalization (2NF)'),
'strDone' => __('Done'),
'strConfirmPd' => __('Confirm partial dependencies'),
'strSelectedPd' => __('Selected partial dependencies are as follows:'),
'strPdHintNote' => __(
'Note: a, b -> d,f implies values of columns a and b combined together can '
. 'determine values of column d and column f.'
),
'strNoPdSelected' => __('No partial dependencies selected!'),
'strBack' => __('Back'),
'strShowPossiblePd' => __('Show me the possible partial dependencies based on data in the table'),
'strHidePd' => __('Hide partial dependencies list'),
'strWaitForPd' => __(
'Sit tight! It may take few seconds depending on data size and column count of the table.'
),
'strStep' => __('Step'),
'strMoveRepeatingGroup' => '<ol><b>' . __('The following actions will be performed:') . '</b>'
. '<li>' . __('DROP columns %s from the table %s') . '</li>'
. '<li>' . __('Create the following table') . '</li>',
'strNewTablePlaceholder' => 'Enter new table name',
'strNewColumnPlaceholder' => 'Enter column name',
'str3NFNormalization' => __('Third step of normalization (3NF)'),
'strConfirmTd' => __('Confirm transitive dependencies'),
'strSelectedTd' => __('Selected dependencies are as follows:'),
'strNoTdSelected' => __('No dependencies selected!'),
/* For server/variables.js */
'strSave' => __('Save'),
/* For table/select.js */
'strHideSearchCriteria' => __('Hide search criteria'),
'strShowSearchCriteria' => __('Show search criteria'),
'strColumnMax' => __('Column maximum:'),
'strColumnMin' => __('Column minimum:'),
/* For table/find_replace.js */
'strHideFindNReplaceCriteria' => __('Hide find and replace criteria'),
'strShowFindNReplaceCriteria' => __('Show find and replace criteria'),
/* For table/zoom_plot_jqplot.js */
'strDisplayHelp' => '<ul><li>'
. __('Each point represents a data row.')
. '</li><li>'
. __('Hovering over a point will show its label.')
. '</li><li>'
. __('To zoom in, select a section of the plot with the mouse.')
. '</li><li>'
. __('Click reset zoom button to come back to original state.')
. '</li><li>'
. __('Click a data point to view and possibly edit the data row.')
. '</li><li>'
. __('The plot can be resized by dragging it along the bottom right corner.')
. '</li></ul>',
'strHelpTitle' => 'Zoom search instructions',
'strInputNull' => '<strong>' . __('Select two columns') . '</strong>',
'strSameInputs' => '<strong>'
. __('Select two different columns')
. '</strong>',
'strDataPointContent' => __('Data point content'),
/* For table/change.js */
'strIgnore' => __('Ignore'),
'strCopy' => __('Copy'),
'strX' => __('X'),
'strY' => __('Y'),
'strPoint' => __('Point'),
'strPointN' => __('Point %d'),
'strLineString' => __('Linestring'),
'strPolygon' => __('Polygon'),
'strGeometry' => __('Geometry'),
'strInnerRing' => __('Inner ring'),
'strOuterRing' => __('Outer ring'),
'strAddPoint' => __('Add a point'),
'strAddInnerRing' => __('Add an inner ring'),
'strYes' => __('Yes'),
'strCopyEncryptionKey' => __('Do you want to copy encryption key?'),
'strEncryptionKey' => __('Encryption key'),
/* l10n: Tip for HEX conversion of Integers */
'HexConversionInfo' => __(
'The HEX function will treat the integer as a string while calculating the hexadecimal value'
),
/* For Tip to be shown on Time field */
'strMysqlAllowedValuesTipTime' => __(
'MySQL accepts additional values not selectable by the slider;'
. ' key in those values directly if desired'
),
/* For Tip to be shown on Date field */
'strMysqlAllowedValuesTipDate' => __(
'MySQL accepts additional values not selectable by the datepicker;'
. ' key in those values directly if desired'
),
/* For Lock symbol Tooltip */
'strLockToolTip' => __(
'Indicates that you have made changes to this page;'
. ' you will be prompted for confirmation before abandoning changes'
),
/* Designer (js/designer/move.js) */
'strSelectReferencedKey' => __('Select referenced key'),
'strSelectForeignKey' => __('Select Foreign Key'),
'strPleaseSelectPrimaryOrUniqueKey' => __('Please select the primary key or a unique key!'),
'strChangeDisplay' => __('Choose column to display'),
'strLeavingDesigner' => __(
'You haven\'t saved the changes in the layout. They will be lost if you'
. ' don\'t save them. Do you want to continue?'
),
'strQueryEmpty' => __('value/subQuery is empty'),
'strAddTables' => __('Add tables from other databases'),
'strPageName' => __('Page name'),
'strSavePage' => __('Save page'),
'strSavePageAs' => __('Save page as'),
'strOpenPage' => __('Open page'),
'strDeletePage' => __('Delete page'),
/* l10n: When the user opens a page saved in the Designer */
'strSavedPageTableMissing' => __('Some tables saved in this page might have been renamed or deleted.'),
'strUntitled' => __('Untitled'),
'strSelectPage' => __('Please select a page to continue'),
'strEnterValidPageName' => __('Please enter a valid page name'),
'strLeavingPage' => __('Do you want to save the changes to the current page?'),
'strSuccessfulPageDelete' => __('Successfully deleted the page'),
'strExportRelationalSchema' => __('Export relational schema'),
'strModificationSaved' => __('Modifications have been saved'),
/* Visual query builder (js/designer/move.js) */
'strObjectsCreated' => __('%d object(s) created.'),
'strColumnName' => __('Column name'),
'strSubmit' => __('Submit'),
/* For makegrid.js (column reordering, show/hide column, grid editing) */
'strCellEditHint' => __('Press escape to cancel editing.<br>- Shift+Enter for a newline.'),
'strSaveCellWarning' => __(
'You have edited some data and they have not been saved. Are you sure you want '
. 'to leave this page before saving the data?'
),
'strColOrderHint' => __('Drag to reorder.'),
'strSortHint' => __('Click to sort results by this column.'),
'strMultiSortHint' => __(
'Shift+Click to add this column to ORDER BY clause or to toggle ASC/DESC.'
. '<br>- Ctrl+Click or Alt+Click (Mac: Shift+Option+Click) to remove column '
. 'from ORDER BY clause'
),
'strColMarkHint' => __('Click to mark/unmark.'),
'strColNameCopyHint' => __('Double-click to copy column name.'),
'strColVisibHint' => __('Click the drop-down arrow<br>to toggle column\'s visibility.'),
'strShowAllCol' => __('Show all'),
'strAlertNonUnique' => __(
'This table does not contain a unique column. Features related to the grid '
. 'edit, checkbox, Edit, Copy and Delete links may not work after saving.'
),
'strEnterValidHex' => __('Please enter a valid hexadecimal string. Valid characters are 0-9, A-F.'),
'strShowAllRowsWarning' => __(
'Do you really want to see all of the rows? For a big table this could crash the browser.'
),
'strOriginalLength' => __('Original length'),
/* Drag & Drop sql import messages */
'dropImportMessageCancel' => __('cancel'),
'dropImportMessageAborted' => __('Aborted'),
'dropImportMessageFailed' => __('Failed'),
'dropImportMessageSuccess' => __('Success'),
'dropImportImportResultHeader' => __('Import status'),
'dropImportDropFiles' => __('Drop files here'),
'dropImportSelectDB' => __('Select database first'),
// this approach does not work when the parameter is changed via user prefs
'strGridEditFeatureHint' => $cfg['GridEditing'] === 'double-click'
? __('You can also edit most values<br>by double-clicking directly on them.')
: ($cfg['GridEditing'] === 'click'
? __('You can also edit most values<br>by clicking directly on them.')
: ''),
'strGoToLink' => __('Go to link:'),
/* password generation */
'strGeneratePassword' => __('Generate password'),
'strGenerate' => __('Generate'),
'strChangePassword' => __('Change password'),
/* navigation tabs */
'strMore' => __('More'),
/* navigation panel */
'strShowPanel' => __('Show panel'),
'strHidePanel' => __('Hide panel'),
'linkWithMain' => __('Link with main panel'),
'unlinkWithMain' => __('Unlink from main panel'),
/* update */
'strNewerVersion' => __(
'A newer version of phpMyAdmin is available and you should consider upgrading. '
. 'The newest version is %s, released on %s.'
),
/* l10n: Latest available phpMyAdmin version */
'strLatestAvailable' => __(', latest stable version:'),
'strUpToDate' => __('up to date'),
/* Error Reporting */
'strErrorOccurred' => __('A fatal JavaScript error has occurred. Would you like to send an error report?'),
'strChangeReportSettings' => __('Change report settings'),
'strShowReportDetails' => __('Show report details'),
'strTimeOutError' => __('Your export is incomplete, due to a low execution time limit at the PHP level!'),
'strTooManyInputs' => __(
'Warning: a form on this page has more than %d fields. On submission, '
. "some of the fields might be ignored, due to PHP's "
. 'max_input_vars configuration.'
),
'phpErrorsFound' => '<div class="alert alert-danger" role="alert">'
. __('Some errors have been detected on the server!')
. '<br>'
. __('Please look at the bottom of this window.')
. '<div>'
. '<input id="pma_ignore_errors_popup" type="submit" value="'
. __('Ignore')
. '" class="btn btn-secondary float-end message_errors_found">'
. '<input id="pma_ignore_all_errors_popup" type="submit" value="'
. __('Ignore All')
. '" class="btn btn-secondary float-end message_errors_found">'
. '</div></div>',
'phpErrorsBeingSubmitted' => '<div class="alert alert-danger" role="alert">'
. __('Some errors have been detected on the server!')
. '<br>'
. __('As per your settings, they are being submitted currently, please be patient.')
. '<br>'
. '<img src="'
. $ajaxClockSmallGifPath
. '" width="16" height="16" alt="ajax clock">'
. '</div>',
'strCopyColumnSuccess' => __('Column name successfully copied to clipboard!'),
'strCopyColumnFailure' => __('Column name copying to clipboard failed!'),
'strCopyQueryButtonSuccess' => __('Successfully copied!'),
'strCopyQueryButtonFailure' => __('Copying failed!'),
// For console
'strConsoleRequeryConfirm' => __('Execute this query again?'),
'strConsoleDeleteBookmarkConfirm' => __('Do you really want to delete this bookmark?'),
'strConsoleDebugError' => __('Some error occurred while getting SQL debug info.'),
'strConsoleDebugSummary' => __('%s queries executed %s times in %s seconds.'),
'strConsoleDebugArgsSummary' => __('%s argument(s) passed'),
'strConsoleDebugShowArgs' => __('Show arguments'),
'strConsoleDebugHideArgs' => __('Hide arguments'),
'strConsoleDebugTimeTaken' => __('Time taken:'),
'strNoLocalStorage' => __(
'There was a problem accessing your browser storage, some features may not'
. ' work properly for you. It is likely that the browser doesn\'t support storage'
. ' or the quota limit has been reached. In Firefox, corrupted storage can also'
. ' cause such a problem, clearing your "Offline Website Data" might help. In Safari,'
. ' such problem is commonly caused by "Private Mode Browsing".'
),
// For modals in /database/structure
'strCopyTablesTo' => __('Copy tables to'),
'strAddPrefix' => __('Add table prefix'),
'strReplacePrefix' => __('Replace table with prefix'),
'strCopyPrefix' => __('Copy table with prefix'),
/* For password strength simulation */
'strExtrWeak' => __('Extremely weak'),
'strVeryWeak' => __('Very weak'),
'strWeak' => __('Weak'),
'strGood' => __('Good'),
'strStrong' => __('Strong'),
/* U2F errors */
// l10n: error code 5 (from U2F API)
'strU2FTimeout' => _pgettext('U2F error', 'Timed out waiting for security key activation.'),
// l10n: error code 2 (from U2F API)
'strU2FBadRequest' => _pgettext('U2F error', 'Invalid request sent to security key.'),
// l10n: unknown error code (from U2F API)
'strU2FUnknown' => _pgettext('U2F error', 'Unknown security key error.'),
// l10n: error code 3 (from U2F API)
'strU2FInvalidClient' => _pgettext('U2F error', 'Client does not support security key.'),
// l10n: error code 4 (from U2F API) on register
'strU2FErrorRegister' => _pgettext('U2F error', 'Failed security key activation.'),
// l10n: error code 4 (from U2F API) on authanticate
'strU2FErrorAuthenticate' => _pgettext('U2F error', 'Invalid security key.'),
'webAuthnNotSupported' => __(
'WebAuthn is not available. Please use a supported browser in a secure context (HTTPS).'
),
/* Designer */
'strIndexedDBNotWorking' => __(
'You can not open, save or delete your page layout, as IndexedDB is not working'
. ' in your browser and your phpMyAdmin configuration storage is not configured for this.'
),
'strTableAlreadyExists' => _pgettext(
'The table already exists in the designer and can not be added once more.',
'Table %s already exists!'
),
'strHide' => __('Hide'),
'strShow' => __('Show'),
'strStructure' => __('Structure'),
];
}
}

View file

@ -1,42 +0,0 @@
<?php
/**
* Simple script to set correct charset for the license
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use function __;
use function is_readable;
use function printf;
use function readfile;
/**
* Simple script to set correct charset for the license
*/
class LicenseController extends AbstractController
{
public function __invoke(): void
{
$this->response->disable();
$this->response->header('Content-type: text/plain; charset=utf-8');
$filename = LICENSE_FILE;
// Check if the file is available, some distributions remove these.
if (@is_readable($filename)) {
readfile($filename);
return;
}
printf(
__(
'The %s file is not available on this system, please visit %s for more information.'
),
$filename,
'https://www.phpmyadmin.net/'
);
}
}

View file

@ -1,61 +0,0 @@
<?php
/**
* Represents the interface between the linter and the query editor.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
use PhpMyAdmin\Linter;
use function json_encode;
/**
* Represents the interface between the linter and the query editor.
*/
class LintController extends AbstractController
{
public function __invoke(): void
{
$params = [
'sql_query' => $_POST['sql_query'] ?? null,
'options' => $_POST['options'] ?? null,
];
/**
* The SQL query to be analyzed.
*
* This does not need to be checked again XSS or MySQL injections because it is
* never executed, just parsed.
*
* The client, which will receive the JSON response will decode the message and
* and any HTML fragments that are displayed to the user will be encoded anyway.
*
* @var string
*/
$sqlQuery = ! empty($params['sql_query']) ? $params['sql_query'] : '';
$this->response->setAjax(true);
// Disabling standard response.
$this->response->disable();
Core::headerJSON();
if (! empty($params['options'])) {
$options = $params['options'];
if (! empty($options['routineEditor'])) {
$sqlQuery = 'CREATE PROCEDURE `a`() ' . $sqlQuery;
} elseif (! empty($options['triggerEditor'])) {
$sqlQuery = 'CREATE TRIGGER `a` AFTER INSERT ON `b` FOR EACH ROW ' . $sqlQuery;
} elseif (! empty($options['eventEditor'])) {
$sqlQuery = 'CREATE EVENT `a` ON SCHEDULE EVERY MINUTE DO ' . $sqlQuery;
}
}
echo json_encode(Linter::lint($sqlQuery));
}
}

View file

@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
class LogoutController
{
public function __invoke(): void
{
global $auth_plugin, $token_mismatch;
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST' || $token_mismatch) {
Core::sendHeaderLocation('./index.php?route=/');
return;
}
$auth_plugin->logOut();
}
}

View file

@ -1,107 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Config\PageSettings;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Message;
use PhpMyAdmin\Navigation\Navigation;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Utils\SessionCache;
use function __;
/**
* The navigation panel
*
* Displays server, database and table selection tree.
*/
class NavigationController extends AbstractController
{
/** @var Navigation */
private $navigation;
/** @var Relation */
private $relation;
public function __construct(
ResponseRenderer $response,
Template $template,
Navigation $navigation,
Relation $relation
) {
parent::__construct($response, $template);
$this->navigation = $navigation;
$this->relation = $relation;
}
public function __invoke(): void
{
if (! $this->response->isAjax()) {
$this->response->addHTML(
Message::error(
__('Fatal error: The navigation can only be accessed via AJAX')
)->getDisplay()
);
return;
}
if (isset($_POST['getNaviSettings']) && $_POST['getNaviSettings']) {
$pageSettings = new PageSettings('Navi', 'pma_navigation_settings');
$this->response->addHTML($pageSettings->getErrorHTML());
$this->response->addJSON('message', $pageSettings->getHTML());
return;
}
if (isset($_POST['reload'])) {
SessionCache::set('dbs_to_test', false);// Empty database list cache, see #14252
}
$relationParameters = $this->relation->getRelationParameters();
if ($relationParameters->navigationItemsHidingFeature !== null) {
if (isset($_POST['hideNavItem'])) {
if (! empty($_POST['itemName']) && ! empty($_POST['itemType']) && ! empty($_POST['dbName'])) {
$this->navigation->hideNavigationItem(
$_POST['itemName'],
$_POST['itemType'],
$_POST['dbName'],
(! empty($_POST['tableName']) ? $_POST['tableName'] : null)
);
}
return;
}
if (isset($_POST['unhideNavItem'])) {
if (! empty($_POST['itemName']) && ! empty($_POST['itemType']) && ! empty($_POST['dbName'])) {
$this->navigation->unhideNavigationItem(
$_POST['itemName'],
$_POST['itemType'],
$_POST['dbName'],
(! empty($_POST['tableName']) ? $_POST['tableName'] : null)
);
}
return;
}
if (isset($_POST['showUnhideDialog'])) {
if (! empty($_POST['dbName'])) {
$this->response->addJSON(
'message',
$this->navigation->getItemUnhideDialog($_POST['dbName'])
);
}
return;
}
}
$this->response->addJSON('message', $this->navigation->getDisplay());
}
}

View file

@ -1,166 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
use PhpMyAdmin\Normalization;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function __;
use function _pgettext;
use function in_array;
use function intval;
use function json_decode;
use function json_encode;
use function min;
/**
* Normalization process (temporarily specific to 1NF).
*/
class NormalizationController extends AbstractController
{
/** @var Normalization */
private $normalization;
public function __construct(ResponseRenderer $response, Template $template, Normalization $normalization)
{
parent::__construct($response, $template);
$this->normalization = $normalization;
}
public function __invoke(): void
{
global $db, $table;
if (isset($_POST['getColumns'])) {
$html = '<option selected disabled>' . __('Select one…') . '</option>'
. '<option value="no_such_col">' . __('No such column') . '</option>';
//get column whose datatype falls under string category
$html .= $this->normalization->getHtmlForColumnsList(
$db,
$table,
_pgettext('string types', 'String')
);
echo $html;
return;
}
if (isset($_POST['splitColumn'])) {
$num_fields = min(4096, intval($_POST['numFields']));
$html = $this->normalization->getHtmlForCreateNewColumn($num_fields, $db, $table);
$html .= Url::getHiddenInputs($db, $table);
echo $html;
return;
}
if (isset($_POST['addNewPrimary'])) {
$num_fields = 1;
$columnMeta = [
'Field' => $table . '_id',
'Extra' => 'auto_increment',
];
$html = $this->normalization->getHtmlForCreateNewColumn($num_fields, $db, $table, $columnMeta);
$html .= Url::getHiddenInputs($db, $table);
echo $html;
return;
}
if (isset($_POST['findPdl'])) {
$html = $this->normalization->findPartialDependencies($table, $db);
echo $html;
return;
}
if (isset($_POST['getNewTables2NF'])) {
$partialDependencies = json_decode($_POST['pd'], true);
$html = $this->normalization->getHtmlForNewTables2NF($partialDependencies, $table);
echo $html;
return;
}
if (isset($_POST['getNewTables3NF'])) {
$dependencies = json_decode($_POST['pd']);
$tables = json_decode($_POST['tables'], true);
$newTables = $this->normalization->getHtmlForNewTables3NF($dependencies, $tables, $db);
$this->response->disable();
Core::headerJSON();
echo json_encode($newTables);
return;
}
$this->addScriptFiles(['normalization.js', 'vendor/jquery/jquery.uitablefilter.js']);
$normalForm = '1nf';
if (isset($_POST['normalizeTo']) && in_array($_POST['normalizeTo'], ['1nf', '2nf', '3nf'])) {
$normalForm = $_POST['normalizeTo'];
}
if (isset($_POST['createNewTables2NF'])) {
$partialDependencies = json_decode($_POST['pd'], true);
$tablesName = json_decode($_POST['newTablesName']);
$res = $this->normalization->createNewTablesFor2NF($partialDependencies, $tablesName, $table, $db);
$this->response->addJSON($res);
return;
}
if (isset($_POST['createNewTables3NF'])) {
$newtables = json_decode($_POST['newTables'], true);
$res = $this->normalization->createNewTablesFor3NF($newtables, $db);
$this->response->addJSON($res);
return;
}
if (isset($_POST['repeatingColumns'])) {
$repeatingColumns = $_POST['repeatingColumns'];
$newTable = $_POST['newTable'];
$newColumn = $_POST['newColumn'];
$primary_columns = $_POST['primary_columns'];
$res = $this->normalization->moveRepeatingGroup(
$repeatingColumns,
$primary_columns,
$newTable,
$newColumn,
$table,
$db
);
$this->response->addJSON($res);
return;
}
if (isset($_POST['step1'])) {
$html = $this->normalization->getHtmlFor1NFStep1($db, $table, $normalForm);
$this->response->addHTML($html);
} elseif (isset($_POST['step2'])) {
$res = $this->normalization->getHtmlContentsFor1NFStep2($db, $table);
$this->response->addJSON($res);
} elseif (isset($_POST['step3'])) {
$res = $this->normalization->getHtmlContentsFor1NFStep3($db, $table);
$this->response->addJSON($res);
} elseif (isset($_POST['step4'])) {
$res = $this->normalization->getHtmlContentsFor1NFStep4($db, $table);
$this->response->addJSON($res);
} elseif (isset($_POST['step']) && $_POST['step'] == '2.1') {
$res = $this->normalization->getHtmlFor2NFstep1($db, $table);
$this->response->addJSON($res);
} elseif (isset($_POST['step']) && $_POST['step'] == '3.1') {
$tables = $_POST['tables'];
$res = $this->normalization->getHtmlFor3NFstep1($db, $tables);
$this->response->addJSON($res);
} else {
$this->response->addHTML($this->normalization->getHtmlForNormalizeTable());
}
}
}

View file

@ -1,34 +0,0 @@
<?php
/**
* phpinfo() wrapper to allow displaying only when configured to do so.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use function phpinfo;
use const INFO_CONFIGURATION;
use const INFO_GENERAL;
use const INFO_MODULES;
/**
* phpinfo() wrapper to allow displaying only when configured to do so.
*/
class PhpInfoController extends AbstractController
{
public function __invoke(): void
{
global $cfg;
$this->response->disable();
$this->response->getHeader()->sendHttpHeaders();
if (! $cfg['ShowPhpInfo']) {
return;
}
phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES);
}
}

View file

@ -1,114 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\ExportForm;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use PhpMyAdmin\Url;
use PhpMyAdmin\UserPreferences;
use function define;
use function ltrim;
class ExportController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cfg, $cf, $error, $tabHash, $hash, $server, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$formDisplay = new ExportForm($cf, 1);
if (isset($_POST['revert'])) {
// revert erroneous fields to their default values
$formDisplay->fixErrors();
$this->redirect('/preferences/export');
return;
}
$error = null;
if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
// Load 2FA settings
$twoFactor = new TwoFactor($cfg['Server']['user']);
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
// save back the 2FA setting only
$twoFactor->save();
if ($result === true) {
// reload config
$this->config->loadUserPreferences();
$tabHash = $_POST['tab_hash'] ?? null;
$hash = ltrim($tabHash, '#');
$this->userPreferences->redirect('index.php?route=/preferences/export', null, $hash);
return;
}
$error = $result;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
$this->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($formDisplay->hasErrors()) {
$formErrors = $formDisplay->displayErrors();
}
$this->render('preferences/forms/main', [
'error' => $error ? $error->getDisplay() : '',
'has_errors' => $formDisplay->hasErrors(),
'errors' => $formErrors ?? null,
'form' => $formDisplay->getDisplay(
true,
Url::getFromRoute('/preferences/export'),
['server' => $server]
),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
} else {
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}
}

View file

@ -1,114 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\FeaturesForm;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use PhpMyAdmin\Url;
use PhpMyAdmin\UserPreferences;
use function define;
use function ltrim;
class FeaturesController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cfg, $cf, $error, $tabHash, $hash, $server, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$formDisplay = new FeaturesForm($cf, 1);
if (isset($_POST['revert'])) {
// revert erroneous fields to their default values
$formDisplay->fixErrors();
$this->redirect('/preferences/features');
return;
}
$error = null;
if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
// Load 2FA settings
$twoFactor = new TwoFactor($cfg['Server']['user']);
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
// save back the 2FA setting only
$twoFactor->save();
if ($result === true) {
// reload config
$this->config->loadUserPreferences();
$tabHash = $_POST['tab_hash'] ?? null;
$hash = ltrim($tabHash, '#');
$this->userPreferences->redirect('index.php?route=/preferences/features', null, $hash);
return;
}
$error = $result;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
$this->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($formDisplay->hasErrors()) {
$formErrors = $formDisplay->displayErrors();
}
$this->render('preferences/forms/main', [
'error' => $error ? $error->getDisplay() : '',
'has_errors' => $formDisplay->hasErrors(),
'errors' => $formErrors ?? null,
'form' => $formDisplay->getDisplay(
true,
Url::getFromRoute('/preferences/features'),
['server' => $server]
),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
} else {
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}
}

View file

@ -1,114 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\ImportForm;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use PhpMyAdmin\Url;
use PhpMyAdmin\UserPreferences;
use function define;
use function ltrim;
class ImportController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cfg, $cf, $error, $tabHash, $hash, $server, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$formDisplay = new ImportForm($cf, 1);
if (isset($_POST['revert'])) {
// revert erroneous fields to their default values
$formDisplay->fixErrors();
$this->redirect('/preferences/import');
return;
}
$error = null;
if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
// Load 2FA settings
$twoFactor = new TwoFactor($cfg['Server']['user']);
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
// save back the 2FA setting only
$twoFactor->save();
if ($result === true) {
// reload config
$this->config->loadUserPreferences();
$tabHash = $_POST['tab_hash'] ?? null;
$hash = ltrim($tabHash, '#');
$this->userPreferences->redirect('index.php?route=/preferences/import', null, $hash);
return;
}
$error = $result;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
$this->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($formDisplay->hasErrors()) {
$formErrors = $formDisplay->displayErrors();
}
$this->render('preferences/forms/main', [
'error' => $error ? $error->getDisplay() : '',
'has_errors' => $formDisplay->hasErrors(),
'errors' => $formErrors ?? null,
'form' => $formDisplay->getDisplay(
true,
Url::getFromRoute('/preferences/import'),
['server' => $server]
),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
} else {
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}
}

View file

@ -1,114 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\MainForm;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use PhpMyAdmin\Url;
use PhpMyAdmin\UserPreferences;
use function define;
use function ltrim;
class MainPanelController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cfg, $cf, $error, $tabHash, $hash, $server, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$formDisplay = new MainForm($cf, 1);
if (isset($_POST['revert'])) {
// revert erroneous fields to their default values
$formDisplay->fixErrors();
$this->redirect('/preferences/main-panel');
return;
}
$error = null;
if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
// Load 2FA settings
$twoFactor = new TwoFactor($cfg['Server']['user']);
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
// save back the 2FA setting only
$twoFactor->save();
if ($result === true) {
// reload config
$this->config->loadUserPreferences();
$tabHash = $_POST['tab_hash'] ?? null;
$hash = ltrim($tabHash, '#');
$this->userPreferences->redirect('index.php?route=/preferences/main-panel', null, $hash);
return;
}
$error = $result;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
$this->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($formDisplay->hasErrors()) {
$formErrors = $formDisplay->displayErrors();
}
$this->render('preferences/forms/main', [
'error' => $error ? $error->getDisplay() : '',
'has_errors' => $formDisplay->hasErrors(),
'errors' => $formErrors ?? null,
'form' => $formDisplay->getDisplay(
true,
Url::getFromRoute('/preferences/main-panel'),
['server' => $server]
),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
} else {
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}
}

View file

@ -1,283 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\UserFormList;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Core;
use PhpMyAdmin\File;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\ThemeManager;
use PhpMyAdmin\UserPreferences;
use PhpMyAdmin\Util;
use function __;
use function array_merge;
use function define;
use function file_exists;
use function is_array;
use function is_string;
use function is_uploaded_file;
use function json_decode;
use function json_encode;
use function mb_strpos;
use function mb_substr;
use function parse_url;
use function str_replace;
use function urlencode;
use function var_export;
use const JSON_PRETTY_PRINT;
use const PHP_URL_PATH;
use const UPLOAD_ERR_OK;
/**
* User preferences management page.
*/
class ManageController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cf, $error, $filename, $json, $lang;
global $new_config, $return_url, $form_display, $all_ok, $params, $query, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$error = '';
if (isset($_POST['submit_export'], $_POST['export_type']) && $_POST['export_type'] === 'text_file') {
// export to JSON file
$this->response->disable();
$filename = 'phpMyAdmin-config-' . urlencode(Core::getenv('HTTP_HOST')) . '.json';
Core::downloadHeader($filename, 'application/json');
$settings = $this->userPreferences->load();
echo json_encode($settings['config_data'], JSON_PRETTY_PRINT);
return;
}
if (isset($_POST['submit_export'], $_POST['export_type']) && $_POST['export_type'] === 'php_file') {
// export to JSON file
$this->response->disable();
$filename = 'phpMyAdmin-config-' . urlencode(Core::getenv('HTTP_HOST')) . '.php';
Core::downloadHeader($filename, 'application/php');
$settings = $this->userPreferences->load();
echo '/* ' . __('phpMyAdmin configuration snippet') . " */\n\n";
echo '/* ' . __('Paste it to your config.inc.php') . " */\n\n";
foreach ($settings['config_data'] as $key => $val) {
echo '$cfg[\'' . str_replace('/', '\'][\'', $key) . '\'] = ';
echo var_export($val, true) . ";\n";
}
return;
}
if (isset($_POST['submit_get_json'])) {
$settings = $this->userPreferences->load();
$this->response->addJSON('prefs', json_encode($settings['config_data']));
$this->response->addJSON('mtime', $settings['mtime']);
return;
}
if (isset($_POST['submit_import'])) {
// load from JSON file
$json = '';
if (
isset($_POST['import_type'], $_FILES['import_file'])
&& $_POST['import_type'] === 'text_file'
&& is_array($_FILES['import_file'])
&& $_FILES['import_file']['error'] == UPLOAD_ERR_OK
&& isset($_FILES['import_file']['tmp_name'])
&& is_string($_FILES['import_file']['tmp_name'])
&& is_uploaded_file($_FILES['import_file']['tmp_name'])
) {
$importHandle = new File($_FILES['import_file']['tmp_name']);
$importHandle->checkUploadedFile();
if ($importHandle->isError()) {
$error = $importHandle->getError();
} else {
// read JSON from uploaded file
$json = $importHandle->getRawContent();
}
} else {
// read from POST value (json)
$json = $_POST['json'] ?? null;
}
// hide header message
$_SESSION['userprefs_autoload'] = true;
$configuration = json_decode($json, true);
$return_url = $_POST['return_url'] ?? null;
if (! is_array($configuration)) {
if (! isset($error)) {
$error = __('Could not import configuration');
}
} else {
// sanitize input values: treat them as though
// they came from HTTP POST request
$form_display = new UserFormList($cf);
$new_config = $cf->getFlatDefaultConfig();
if (! empty($_POST['import_merge'])) {
$new_config = array_merge($new_config, $cf->getConfigArray());
}
$new_config = array_merge($new_config, $configuration);
$_POST_bak = $_POST;
foreach ($new_config as $k => $v) {
$_POST[str_replace('/', '-', (string) $k)] = $v;
}
$cf->resetConfigData();
$all_ok = $form_display->process(true, false);
$all_ok = $all_ok && ! $form_display->hasErrors();
$_POST = $_POST_bak;
if (! $all_ok && isset($_POST['fix_errors'])) {
$form_display->fixErrors();
$all_ok = true;
}
if (! $all_ok) {
// mimic original form and post json in a hidden field
$relationParameters = $this->relation->getRelationParameters();
echo $this->template->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
echo $this->template->render('preferences/manage/error', [
'form_errors' => $form_display->displayErrors(),
'json' => $json,
'import_merge' => $_POST['import_merge'] ?? null,
'return_url' => $return_url,
]);
return;
}
// check for ThemeDefault
$params = [];
$tmanager = ThemeManager::getInstance();
if (
isset($configuration['ThemeDefault'])
&& $tmanager->theme->getId() != $configuration['ThemeDefault']
&& $tmanager->checkTheme($configuration['ThemeDefault'])
) {
$tmanager->setActiveTheme($configuration['ThemeDefault']);
$tmanager->setThemeCookie();
}
if (isset($configuration['lang']) && $configuration['lang'] != $lang) {
$params['lang'] = $configuration['lang'];
}
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
if ($result === true) {
if ($return_url) {
$query = Util::splitURLQuery($return_url);
$return_url = parse_url($return_url, PHP_URL_PATH);
foreach ($query as $q) {
$pos = mb_strpos($q, '=');
$k = mb_substr($q, 0, (int) $pos);
if ($k === 'token') {
continue;
}
$params[$k] = mb_substr($q, $pos + 1);
}
} else {
$return_url = 'index.php?route=/preferences/manage';
}
// reload config
$this->config->loadUserPreferences();
$this->userPreferences->redirect($return_url ?? '', $params);
return;
}
$error = $result;
}
} elseif (isset($_POST['submit_clear'])) {
$result = $this->userPreferences->save([]);
if ($result === true) {
$params = [];
$this->config->removeCookie('pma_collaction_connection');
$this->config->removeCookie('pma_lang');
$this->userPreferences->redirect('index.php?route=/preferences/manage', $params);
return;
} else {
$error = $result;
}
return;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
echo $this->template->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($error) {
if (! $error instanceof Message) {
$error = Message::error($error);
}
$error->getDisplay();
}
echo $this->template->render('preferences/manage/main', [
'error' => $error,
'max_upload_size' => $GLOBALS['config']->get('max_upload_size'),
'exists_setup_and_not_exists_config' => @file_exists(ROOT_PATH . 'setup/index.php')
&& ! @file_exists(CONFIG_FILE),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
} else {
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}
}

View file

@ -1,116 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\NaviForm;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use PhpMyAdmin\Url;
use PhpMyAdmin\UserPreferences;
use function define;
use function ltrim;
class NavigationController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cfg, $cf, $error, $tabHash, $hash, $server, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$formDisplay = new NaviForm($cf, 1);
if (isset($_POST['revert'])) {
// revert erroneous fields to their default values
$formDisplay->fixErrors();
$this->redirect('/preferences/navigation');
return;
}
$error = null;
if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
// Load 2FA settings
$twoFactor = new TwoFactor($cfg['Server']['user']);
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
// save back the 2FA setting only
$twoFactor->save();
if ($result === true) {
// reload config
$this->config->loadUserPreferences();
$tabHash = $_POST['tab_hash'] ?? null;
$hash = ltrim($tabHash, '#');
$this->userPreferences->redirect('index.php?route=/preferences/navigation', null, $hash);
return;
}
$error = $result;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
$this->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($formDisplay->hasErrors()) {
$formErrors = $formDisplay->displayErrors();
}
$this->render('preferences/forms/main', [
'error' => $error ? $error->getDisplay() : '',
'has_errors' => $formDisplay->hasErrors(),
'errors' => $formErrors ?? null,
'form' => $formDisplay->getDisplay(
true,
Url::getFromRoute('/preferences/navigation'),
['server' => $server]
),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
return;
}
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}

View file

@ -1,114 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Forms\User\SqlForm;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use PhpMyAdmin\Url;
use PhpMyAdmin\UserPreferences;
use function define;
use function ltrim;
class SqlController extends AbstractController
{
/** @var UserPreferences */
private $userPreferences;
/** @var Relation */
private $relation;
/** @var Config */
private $config;
public function __construct(
ResponseRenderer $response,
Template $template,
UserPreferences $userPreferences,
Relation $relation,
Config $config
) {
parent::__construct($response, $template);
$this->userPreferences = $userPreferences;
$this->relation = $relation;
$this->config = $config;
}
public function __invoke(): void
{
global $cfg, $cf, $error, $tabHash, $hash, $server, $route;
$cf = new ConfigFile($this->config->baseSettings);
$this->userPreferences->pageInit($cf);
$formDisplay = new SqlForm($cf, 1);
if (isset($_POST['revert'])) {
// revert erroneous fields to their default values
$formDisplay->fixErrors();
$this->redirect('/preferences/sql');
return;
}
$error = null;
if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
// Load 2FA settings
$twoFactor = new TwoFactor($cfg['Server']['user']);
// save settings
$result = $this->userPreferences->save($cf->getConfigArray());
// save back the 2FA setting only
$twoFactor->save();
if ($result === true) {
// reload config
$this->config->loadUserPreferences();
$tabHash = $_POST['tab_hash'] ?? null;
$hash = ltrim($tabHash, '#');
$this->userPreferences->redirect('index.php?route=/preferences/sql', null, $hash);
return;
}
$error = $result;
}
$this->addScriptFiles(['config.js']);
$relationParameters = $this->relation->getRelationParameters();
$this->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
if ($formDisplay->hasErrors()) {
$formErrors = $formDisplay->displayErrors();
}
$this->render('preferences/forms/main', [
'error' => $error ? $error->getDisplay() : '',
'has_errors' => $formDisplay->hasErrors(),
'errors' => $formErrors ?? null,
'form' => $formDisplay->getDisplay(
true,
Url::getFromRoute('/preferences/sql'),
['server' => $server]
),
]);
if ($this->response->isAjax()) {
$this->response->addJSON('disableNaviSettings', true);
} else {
define('PMA_DISABLE_NAVI_SETTINGS', true);
}
}
}

View file

@ -1,77 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\TwoFactor;
use function __;
use function count;
class TwoFactorController extends AbstractController
{
/** @var Relation */
private $relation;
public function __construct(ResponseRenderer $response, Template $template, Relation $relation)
{
parent::__construct($response, $template);
$this->relation = $relation;
}
public function __invoke(): void
{
global $cfg, $route;
$relationParameters = $this->relation->getRelationParameters();
echo $this->template->render('preferences/header', [
'route' => $route,
'is_saved' => ! empty($_GET['saved']),
'has_config_storage' => $relationParameters->userPreferencesFeature !== null,
]);
$twoFactor = new TwoFactor($cfg['Server']['user']);
if (isset($_POST['2fa_remove'])) {
if (! $twoFactor->check(true)) {
echo $this->template->render('preferences/two_factor/confirm', [
'form' => $twoFactor->render(),
]);
return;
}
$twoFactor->configure('');
echo Message::rawNotice(__('Two-factor authentication has been removed.'))->getDisplay();
} elseif (isset($_POST['2fa_configure'])) {
if (! $twoFactor->configure($_POST['2fa_configure'])) {
echo $this->template->render('preferences/two_factor/configure', [
'form' => $twoFactor->setup(),
'configure' => $_POST['2fa_configure'],
]);
return;
}
echo Message::rawNotice(__('Two-factor authentication has been configured.'))->getDisplay();
}
$backend = $twoFactor->getBackend();
echo $this->template->render('preferences/two_factor/main', [
'enabled' => $twoFactor->isWritable(),
'num_backends' => count($twoFactor->getAvailable()),
'backend_id' => $backend::$id,
'backend_name' => $backend::getName(),
'backend_description' => $backend::getDescription(),
'backends' => $twoFactor->getAllBackends(),
'missing' => $twoFactor->getMissingDeps(),
]);
}
}

View file

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\RecentFavoriteTable;
final class RecentTablesListController extends AbstractController
{
public function __invoke(): void
{
if (! $this->response->isAjax()) {
return;
}
$this->response->addJSON([
'list' => RecentFavoriteTable::getInstance('recent')->getHtmlList(),
]);
}
}

View file

@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Export;
use PhpMyAdmin\Util;
/**
* Schema export handler
*/
class SchemaExportController
{
/** @var Export */
private $export;
public function __construct(Export $export)
{
$this->export = $export;
}
public function __invoke(): void
{
if (! isset($_POST['export_type'])) {
Util::checkParameters(['export_type']);
}
/**
* Include the appropriate Schema Class depending on $export_type
* default is PDF
*/
$this->export->processExportSchema($_POST['export_type']);
}
}

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']
),
]);
}
}

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