phpmyadmin/libraries/classes/Menu.php
Maurício Meneghini Fauth 12efd0a0fe
Replace replication statements with new terminology
SHOW MASTER LOGS was removed in MySQL 8.4.0 and SHOW BINARY LOGS is
available since MySQL 4.1.1.

As of MySQL 8.2.0, SHOW BINARY LOG STATUS is available.
As of MariaDB 10.5.2, SHOW BINLOG STATUS is available.

Signed-off-by: Maurício Meneghini Fauth <mauricio@mfauth.net>
2024-10-31 13:50:11 -03:00

583 lines
20 KiB
PHP

<?php
/**
* Generates and renders the top menu
*/
declare(strict_types=1);
namespace PhpMyAdmin;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Query\Utilities;
use PhpMyAdmin\Utils\SessionCache;
use function __;
use function array_intersect_key;
use function count;
use function in_array;
use function mb_strpos;
use function mb_strstr;
use function mb_substr;
use function preg_replace;
/**
* Class for generating the top menu
*/
class Menu
{
/**
* Database name
*
* @var string
*/
private $db;
/** @var DatabaseInterface */
private $dbi;
/**
* Table name
*
* @var string
*/
private $table;
/** @var Relation */
private $relation;
/** @var Template */
private $template;
/**
* Creates a new instance of Menu
*
* @param string $db Database name
* @param string $table Table name
*/
public function __construct(DatabaseInterface $dbi, string $db, string $table)
{
$this->db = $db;
$this->dbi = $dbi;
$this->table = $table;
$this->relation = new Relation($dbi);
$this->template = new Template();
}
/**
* Returns the menu and the breadcrumbs as a string
*/
public function getDisplay(): string
{
$retval = $this->getBreadcrumbs();
$retval .= $this->getMenu();
return $retval;
}
/**
* Returns the menu as HTML
*
* @return string HTML formatted menubar
*/
private function getMenu(): string
{
$urlParams = [];
// The URL will not work if the table is defined without a database
if ($this->table !== '' && $this->db !== '') {
$tabs = $this->getTableTabs();
$urlParams['db'] = $this->db;
$urlParams['table'] = $this->table;
$level = 'table';
} elseif ($this->db !== '') {
$tabs = $this->getDbTabs();
$urlParams['db'] = $this->db;
$level = 'db';
} else {
$tabs = $this->getServerTabs();
$level = 'server';
}
$allowedTabs = $this->getAllowedTabs($level);
// Filter out any tabs that are not allowed
$tabs = array_intersect_key($tabs, $allowedTabs);
return $this->template->render('top_menu', [
'tabs' => $tabs,
'url_params' => $urlParams,
]);
}
/**
* Returns a list of allowed tabs for the current user for the given level
*
* @param string $level 'server', 'db' or 'table' level
*
* @return array list of allowed tabs
*/
private function getAllowedTabs($level)
{
$cacheKey = 'menu-levels-' . $level;
if (SessionCache::has($cacheKey)) {
return SessionCache::get($cacheKey);
}
$allowedTabs = Util::getMenuTabList($level) ?? [];
$configurableMenusFeature = $this->relation->getRelationParameters()->configurableMenusFeature;
if ($configurableMenusFeature !== null) {
$groupTable = Util::backquote($configurableMenusFeature->database)
. '.' . Util::backquote($configurableMenusFeature->userGroups);
$userTable = Util::backquote($configurableMenusFeature->database)
. '.' . Util::backquote($configurableMenusFeature->users);
$sqlQuery = 'SELECT `tab` FROM ' . $groupTable
. " WHERE `allowed` = 'N'"
. " AND `tab` LIKE '" . $level . "%'"
. ' AND `usergroup` = (SELECT usergroup FROM '
. $userTable . " WHERE `username` = '"
. $this->dbi->escapeString($GLOBALS['cfg']['Server']['user']) . "')";
$result = $this->dbi->tryQueryAsControlUser($sqlQuery);
if ($result) {
while ($row = $result->fetchAssoc()) {
$tab = (string) $row['tab'];
$tabName = mb_substr(
$tab,
mb_strpos($tab, '_') + 1
);
unset($allowedTabs[$tabName]);
}
}
}
SessionCache::set($cacheKey, $allowedTabs);
return $allowedTabs;
}
/**
* Returns the breadcrumbs as HTML
*
* @return string HTML formatted breadcrumbs
*/
private function getBreadcrumbs(): string
{
global $cfg;
$server = [];
$database = [];
$table = [];
if (empty($cfg['Server']['host'])) {
$cfg['Server']['host'] = '';
}
$server['name'] = ! empty($cfg['Server']['verbose'])
? $cfg['Server']['verbose'] : $cfg['Server']['host'];
$server['name'] .= empty($cfg['Server']['port'])
? '' : ':' . $cfg['Server']['port'];
$server['url'] = Util::getUrlForOption($cfg['DefaultTabServer'], 'server');
if ($this->db !== '') {
$database['name'] = $this->db;
$database['url'] = Util::getUrlForOption($cfg['DefaultTabDatabase'], 'database');
if ($this->table !== '') {
$table['name'] = $this->table;
$table['url'] = Util::getUrlForOption($cfg['DefaultTabTable'], 'table');
$tableObj = $this->dbi->getTable($this->db, $this->table);
$table['is_view'] = $tableObj->isView();
$table['comment'] = '';
if (! $table['is_view']) {
$table['comment'] = $tableObj->getComment();
}
if (mb_strstr($table['comment'], '; InnoDB free')) {
$table['comment'] = preg_replace('@; InnoDB free:.*?$@', '', $table['comment']);
}
} else {
// no table selected, display database comment if present
$relationParameters = $this->relation->getRelationParameters();
// Get additional information about tables for tooltip is done
// in Util::getDbInfo() only once
if ($relationParameters->columnCommentsFeature !== null) {
$database['comment'] = $this->relation->getDbComment($this->db);
}
}
}
return $this->template->render('menu/breadcrumbs', [
'server' => $server,
'database' => $database,
'table' => $table,
]);
}
/**
* Returns the table tabs as an array
*
* @return array Data for generating table tabs
*/
private function getTableTabs(): array
{
global $route;
$isSystemSchema = Utilities::isSystemSchema($this->db);
$tableIsView = $this->dbi->getTable($this->db, $this->table)
->isView();
$updatableView = false;
if ($tableIsView) {
$updatableView = $this->dbi->getTable($this->db, $this->table)
->isUpdatableView();
}
$isSuperUser = $this->dbi->isSuperUser();
$isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
$tabs = [];
$tabs['browse']['icon'] = 'b_browse';
$tabs['browse']['text'] = __('Browse');
$tabs['browse']['route'] = '/sql';
$tabs['browse']['args']['pos'] = 0;
$tabs['browse']['active'] = $route === '/sql';
$tabs['structure']['icon'] = 'b_props';
$tabs['structure']['route'] = '/table/structure';
$tabs['structure']['text'] = __('Structure');
$tabs['structure']['active'] = in_array($route, [
'/table/relation',
'/table/structure',
]);
$tabs['sql']['icon'] = 'b_sql';
$tabs['sql']['route'] = '/table/sql';
$tabs['sql']['text'] = __('SQL');
$tabs['sql']['active'] = $route === '/table/sql';
$tabs['search']['icon'] = 'b_search';
$tabs['search']['text'] = __('Search');
$tabs['search']['route'] = '/table/search';
$tabs['search']['active'] = in_array($route, [
'/table/find-replace',
'/table/search',
'/table/zoom-search',
]);
if (! $isSystemSchema && (! $tableIsView || $updatableView)) {
$tabs['insert']['icon'] = 'b_insrow';
$tabs['insert']['route'] = '/table/change';
$tabs['insert']['text'] = __('Insert');
$tabs['insert']['active'] = $route === '/table/change';
}
$tabs['export']['icon'] = 'b_tblexport';
$tabs['export']['route'] = '/table/export';
$tabs['export']['args']['single_table'] = 'true';
$tabs['export']['text'] = __('Export');
$tabs['export']['active'] = $route === '/table/export';
/**
* Don't display "Import" for views and information_schema
*/
if (! $tableIsView && ! $isSystemSchema) {
$tabs['import']['icon'] = 'b_tblimport';
$tabs['import']['route'] = '/table/import';
$tabs['import']['text'] = __('Import');
$tabs['import']['active'] = $route === '/table/import';
}
if (($isSuperUser || $isCreateOrGrantUser) && ! $isSystemSchema) {
$tabs['privileges']['route'] = '/server/privileges';
$tabs['privileges']['args']['checkprivsdb'] = $this->db;
$tabs['privileges']['args']['checkprivstable'] = $this->table;
// stay on table view
$tabs['privileges']['args']['viewing_mode'] = 'table';
$tabs['privileges']['text'] = __('Privileges');
$tabs['privileges']['icon'] = 's_rights';
$tabs['privileges']['active'] = $route === '/server/privileges';
}
/**
* Don't display "Operations" for views and information_schema
*/
if (! $tableIsView && ! $isSystemSchema) {
$tabs['operation']['icon'] = 'b_tblops';
$tabs['operation']['route'] = '/table/operations';
$tabs['operation']['text'] = __('Operations');
$tabs['operation']['active'] = $route === '/table/operations';
}
/**
* Views support a limited number of operations
*/
if ($tableIsView && ! $isSystemSchema) {
$tabs['operation']['icon'] = 'b_tblops';
$tabs['operation']['route'] = '/view/operations';
$tabs['operation']['text'] = __('Operations');
$tabs['operation']['active'] = $route === '/view/operations';
}
if (Tracker::isActive() && ! $isSystemSchema) {
$tabs['tracking']['icon'] = 'eye';
$tabs['tracking']['text'] = __('Tracking');
$tabs['tracking']['route'] = '/table/tracking';
$tabs['tracking']['active'] = $route === '/table/tracking';
}
if (! $isSystemSchema && Util::currentUserHasPrivilege('TRIGGER', $this->db, $this->table) && ! $tableIsView) {
$tabs['triggers']['route'] = '/table/triggers';
$tabs['triggers']['text'] = __('Triggers');
$tabs['triggers']['icon'] = 'b_triggers';
$tabs['triggers']['active'] = $route === '/table/triggers';
}
return $tabs;
}
/**
* Returns the db tabs as an array
*
* @return array Data for generating db tabs
*/
private function getDbTabs(): array
{
global $route;
$isSystemSchema = Utilities::isSystemSchema($this->db);
$numTables = count($this->dbi->getTables($this->db));
$isSuperUser = $this->dbi->isSuperUser();
$isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
$relationParameters = $this->relation->getRelationParameters();
$tabs = [];
$tabs['structure']['route'] = '/database/structure';
$tabs['structure']['text'] = __('Structure');
$tabs['structure']['icon'] = 'b_props';
$tabs['structure']['active'] = $route === '/database/structure';
$tabs['sql']['route'] = '/database/sql';
$tabs['sql']['text'] = __('SQL');
$tabs['sql']['icon'] = 'b_sql';
$tabs['sql']['active'] = $route === '/database/sql';
$tabs['search']['text'] = __('Search');
$tabs['search']['icon'] = 'b_search';
$tabs['search']['route'] = '/database/search';
$tabs['search']['active'] = $route === '/database/search';
if ($numTables == 0) {
$tabs['search']['warning'] = __('Database seems to be empty!');
}
$tabs['query']['text'] = __('Query');
$tabs['query']['icon'] = 's_db';
$tabs['query']['route'] = '/database/multi-table-query';
$tabs['query']['active'] = $route === '/database/multi-table-query' || $route === '/database/qbe';
if ($numTables == 0) {
$tabs['query']['warning'] = __('Database seems to be empty!');
}
$tabs['export']['text'] = __('Export');
$tabs['export']['icon'] = 'b_export';
$tabs['export']['route'] = '/database/export';
$tabs['export']['active'] = $route === '/database/export';
if ($numTables == 0) {
$tabs['export']['warning'] = __('Database seems to be empty!');
}
if (! $isSystemSchema) {
$tabs['import']['route'] = '/database/import';
$tabs['import']['text'] = __('Import');
$tabs['import']['icon'] = 'b_import';
$tabs['import']['active'] = $route === '/database/import';
$tabs['operation']['route'] = '/database/operations';
$tabs['operation']['text'] = __('Operations');
$tabs['operation']['icon'] = 'b_tblops';
$tabs['operation']['active'] = $route === '/database/operations';
if ($isSuperUser || $isCreateOrGrantUser) {
$tabs['privileges']['route'] = '/server/privileges';
$tabs['privileges']['args']['checkprivsdb'] = $this->db;
// stay on database view
$tabs['privileges']['args']['viewing_mode'] = 'db';
$tabs['privileges']['text'] = __('Privileges');
$tabs['privileges']['icon'] = 's_rights';
$tabs['privileges']['active'] = $route === '/server/privileges';
}
$tabs['routines']['route'] = '/database/routines';
$tabs['routines']['text'] = __('Routines');
$tabs['routines']['icon'] = 'b_routines';
$tabs['routines']['active'] = $route === '/database/routines';
if (Util::currentUserHasPrivilege('EVENT', $this->db)) {
$tabs['events']['route'] = '/database/events';
$tabs['events']['text'] = __('Events');
$tabs['events']['icon'] = 'b_events';
$tabs['events']['active'] = $route === '/database/events';
}
if (Util::currentUserHasPrivilege('TRIGGER', $this->db)) {
$tabs['triggers']['route'] = '/database/triggers';
$tabs['triggers']['text'] = __('Triggers');
$tabs['triggers']['icon'] = 'b_triggers';
$tabs['triggers']['active'] = $route === '/database/triggers';
}
}
if (Tracker::isActive() && ! $isSystemSchema) {
$tabs['tracking']['text'] = __('Tracking');
$tabs['tracking']['icon'] = 'eye';
$tabs['tracking']['route'] = '/database/tracking';
$tabs['tracking']['active'] = $route === '/database/tracking';
}
if (! $isSystemSchema) {
$tabs['designer']['text'] = __('Designer');
$tabs['designer']['icon'] = 'b_relations';
$tabs['designer']['route'] = '/database/designer';
$tabs['designer']['active'] = $route === '/database/designer';
}
if (! $isSystemSchema && $relationParameters->centralColumnsFeature !== null) {
$tabs['central_columns']['text'] = __('Central columns');
$tabs['central_columns']['icon'] = 'centralColumns';
$tabs['central_columns']['route'] = '/database/central-columns';
$tabs['central_columns']['active'] = $route === '/database/central-columns';
}
return $tabs;
}
/**
* Returns the server tabs as an array
*
* @return array Data for generating server tabs
*/
private function getServerTabs(): array
{
global $route;
$isSuperUser = $this->dbi->isSuperUser();
$isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
if (SessionCache::has('binary_logs')) {
$binaryLogs = SessionCache::get('binary_logs');
} else {
$binaryLogs = $this->dbi->fetchResult('SHOW BINARY LOGS', 'Log_name');
SessionCache::set('binary_logs', $binaryLogs);
}
$tabs = [];
$tabs['databases']['icon'] = 's_db';
$tabs['databases']['route'] = '/server/databases';
$tabs['databases']['text'] = __('Databases');
$tabs['databases']['active'] = $route === '/server/databases';
$tabs['sql']['icon'] = 'b_sql';
$tabs['sql']['route'] = '/server/sql';
$tabs['sql']['text'] = __('SQL');
$tabs['sql']['active'] = $route === '/server/sql';
$tabs['status']['icon'] = 's_status';
$tabs['status']['route'] = '/server/status';
$tabs['status']['text'] = __('Status');
$tabs['status']['active'] = in_array($route, [
'/server/status',
'/server/status/advisor',
'/server/status/monitor',
'/server/status/processes',
'/server/status/queries',
'/server/status/variables',
]);
if ($isSuperUser || $isCreateOrGrantUser) {
$tabs['rights']['icon'] = 's_rights';
$tabs['rights']['route'] = '/server/privileges';
$tabs['rights']['text'] = __('User accounts');
$tabs['rights']['active'] = in_array($route, [
'/server/privileges',
'/server/user-groups',
]);
$tabs['rights']['args']['viewing_mode'] = 'server';
}
$tabs['export']['icon'] = 'b_export';
$tabs['export']['route'] = '/server/export';
$tabs['export']['text'] = __('Export');
$tabs['export']['active'] = $route === '/server/export';
$tabs['import']['icon'] = 'b_import';
$tabs['import']['route'] = '/server/import';
$tabs['import']['text'] = __('Import');
$tabs['import']['active'] = $route === '/server/import';
$tabs['settings']['icon'] = 'b_tblops';
$tabs['settings']['route'] = '/preferences/manage';
$tabs['settings']['text'] = __('Settings');
$tabs['settings']['active'] = in_array($route, [
'/preferences/export',
'/preferences/features',
'/preferences/import',
'/preferences/main-panel',
'/preferences/manage',
'/preferences/navigation',
'/preferences/sql',
'/preferences/two-factor',
]);
if (! empty($binaryLogs)) {
$tabs['binlog']['icon'] = 's_tbl';
$tabs['binlog']['route'] = '/server/binlog';
$tabs['binlog']['text'] = __('Binary log');
$tabs['binlog']['active'] = $route === '/server/binlog';
}
if ($isSuperUser) {
$tabs['replication']['icon'] = 's_replication';
$tabs['replication']['route'] = '/server/replication';
$tabs['replication']['text'] = __('Replication');
$tabs['replication']['active'] = $route === '/server/replication';
}
$tabs['vars']['icon'] = 's_vars';
$tabs['vars']['route'] = '/server/variables';
$tabs['vars']['text'] = __('Variables');
$tabs['vars']['active'] = $route === '/server/variables';
$tabs['charset']['icon'] = 's_asci';
$tabs['charset']['route'] = '/server/collations';
$tabs['charset']['text'] = __('Charsets');
$tabs['charset']['active'] = $route === '/server/collations';
$tabs['engine']['icon'] = 'b_engine';
$tabs['engine']['route'] = '/server/engines';
$tabs['engine']['text'] = __('Engines');
$tabs['engine']['active'] = $route === '/server/engines';
$tabs['plugins']['icon'] = 'b_plugin';
$tabs['plugins']['route'] = '/server/plugins';
$tabs['plugins']['text'] = __('Plugins');
$tabs['plugins']['active'] = $route === '/server/plugins';
return $tabs;
}
/**
* Set current table
*
* @param string $table Current table
*
* @return Menu
*/
public function setTable(string $table)
{
$this->table = $table;
return $this;
}
}